예제 #1
0
 def _judge_curve_embedding_degree(self, curve):
     d = 1
     for k in range(1, 50 + 1):
         d = (d * curve.p) % curve.n
         if d == 1:
             if (k == 1) or (NumberTheory.is_probable_prime(curve.n)):
                 fail_text = "k = %d" % (k)
             else:
                 fail_text = "k <= %d" % (k)
             literature = LiteratureReference(
                 author=[
                     "Alfred Menezes", "Scott Vanstone", "Tatsuaki Okamoto"
                 ],
                 title=
                 "Reducing Elliptic Curve Logarithms to Logarithms in a Finite Field",
                 year=1991,
                 source="ACM")
             return SecurityJudgement(
                 JudgementCode.
                 X509Cert_PublicKey_ECC_DomainParameters_CurveProperty_LowEmbeddingDegree,
                 "This curve has low embedding degree (%s), it fails the MOV condition. It can be compromised using the probabilistic polynomial-time MOV attack."
                 % (fail_text),
                 bits=0,
                 commonness=Commonness.HIGHLY_UNUSUAL,
                 literature=literature)
     return None
예제 #2
0
 def test_probable_prime(self):
     self.assertTrue(NumberTheory.is_probable_prime(2))
     self.assertTrue(NumberTheory.is_probable_prime(3))
     self.assertTrue(NumberTheory.is_probable_prime(5))
     self.assertTrue(NumberTheory.is_probable_prime(101))
     self.assertFalse(NumberTheory.is_probable_prime(0))
     self.assertFalse(NumberTheory.is_probable_prime(1))
     self.assertFalse(NumberTheory.is_probable_prime(4))
예제 #3
0
 def _select_q(self, p):
     if not self._args.close_q:
         while True:
             yield self._prime_db.get(bitlen=self._q_bitlen,
                                      primetype=self._primetype)
     else:
         q = p
         while True:
             q += 2 * random.randint(1, self._args.q_stepping)
             if NumberTheory.is_probable_prime(q):
                 yield q
예제 #4
0
    def _judge_binary_field_curve(self, curve):
        judgements = SecurityJudgements()

        literature = LiteratureReference(
            author=["Steven D. Galbraith", "Shishay W. Gebregiyorgis"],
            title=
            "Summation polynomial algorithms for elliptic curves in characteristic two",
            year=2014,
            source="Progress in Cryptology -- INDOCRYPT 2014; LNCS 8885")
        judgements += SecurityJudgement(
            JudgementCode.X509Cert_PublicKey_ECC_DomainParameters_BinaryField,
            "Binary finite field elliptic curve is used. Recent advances in cryptography show there might be efficient attacks on such curves, hence it is recommended to use prime-field curves instead.",
            commonness=Commonness.UNUSUAL,
            literature=literature)

        EFpm = curve.n * curve.h
        if (EFpm % 2) == 1:
            # Supersingular: #E(F_p^m) = 1 mod p
            literature = LiteratureReference(
                author=[
                    "Alfred Menezes", "Scott Vanstone", "Tatsuaki Okamoto"
                ],
                title=
                "Reducing Elliptic Curve Logarithms to Logarithms in a Finite Field",
                year=1991,
                source="ACM")
            judgements += SecurityJudgement(
                JudgementCode.
                X509Cert_PublicKey_ECC_DomainParameters_CurveProperty_SupersingularCurve,
                "This curve is supersingular, #E(F_p^m) = 1 mod p. The curve can be attacked using an probabilistic polynomial-time MOV attack.",
                bits=0,
                commonness=Commonness.HIGHLY_UNUSUAL,
                literature=literature)

        if not NumberTheory.is_probable_prime(curve.m):
            literature = LiteratureReference(
                author=[
                    "Jeffrey Hoffstein", "Jill Pipher", "Joseph Silverman"
                ],
                title="An Introduction to Mathematical Cryptography",
                year=2008,
                source="Springer")
            judgements += SecurityJudgement(
                JudgementCode.
                X509Cert_PublicKey_ECC_DomainParameters_CurveProperty_WeilDescent,
                "Binary finite field elliptic curve has a field size that is non-primem, F(2^%d). Weil Descent attacks could be successful.",
                bits=0,
                commonness=Commonness.HIGHLY_UNUSUAL,
                literature=literature)

        return judgements
예제 #5
0
    def __init__(self, cmdname, args):
        BaseAction.__init__(self, cmdname, args)

        if (not self._args.force) and os.path.exists(self._args.outfile):
            raise UnfulfilledPrerequisitesException(
                "File/directory %s already exists. Remove it first or use --force."
                % (self._args.outfile))

        self._prime_db = PrimeDB(self._args.prime_db,
                                 generator_program=self._args.generator)
        q = self._prime_db.get(args.N_bits)
        if self._args.verbose >= 1:
            print("Chosen q = 0x%x" % (q))

        bit_diff = args.L_bits - q.bit_length()
        while True:
            r = NumberTheory.randint_bits(bit_diff, two_msb_set=True)
            p = (r * q) + 1
            if NumberTheory.is_probable_prime(p):
                break
        if self._args.verbose >= 1:
            print("Chosen p = 0x%x" % (p))

        assert (q.bit_length() == args.N_bits)
        assert (p.bit_length() == args.L_bits)
        assert ((p - 1) % q == 0)

        # Non-verifiable method of generating g, see A.2.1 of FIPS 186-4, pg. 41
        e = (p - 1) // q
        while True:
            h = random.randint(2, p - 2)
            g = pow(h, e, p)
            if g == 1:
                continue
            break

        if self._args.verbose >= 1:
            print("Chosen g = 0x%x" % (g))

        dsa_parameters = DSAParameters.create(p=p, q=q, g=g)
        dsa_parameters.write_pemfile(self._args.outfile)
예제 #6
0
    def __init__(self, cmdname, args):
        BaseAction.__init__(self, cmdname, args)

        if (not self._args.force) and os.path.exists(self._args.outfile):
            raise UnfulfilledPrerequisitesException(
                "File/directory %s already exists. Remove it first or use --force."
                % (self._args.outfile))

        if not self._args.gcd_n_phi_n:
            self._primetype = "2msb"
            self._p_bitlen = self._args.bitlen // 2
            self._q_bitlen = self._args.bitlen - self._p_bitlen
        else:
            self._primetype = "3msb"
            self._p_bitlen = self._args.bitlen // 3
            self._q_bitlen = self._args.bitlen - (2 * self._p_bitlen) - 1

        if (self._args.close_q) and (self._p_bitlen != self._q_bitlen):
            raise UnfulfilledPrerequisitesException(
                "Generating a close-q keypair with a %d modulus does't work, because p would have to be %d bit and q %d bit. Choose an even modulus bitlength."
                % (self._args.bitlen, self._p_bitlen, self._q_bitlen))

        if self._args.q_stepping < 1:
            raise InvalidInputException(
                "q-stepping value must be greater or equal to 1, was %d." %
                (self._args.q_stepping))

        self._log.debug("Selecting %s primes with p = %d bit and q = %d bit.",
                        self._primetype, self._p_bitlen, self._q_bitlen)

        self._prime_db = PrimeDB(self._args.prime_db,
                                 generator_program=self._args.generator)
        p = None
        q = None
        while True:
            if p is None:
                p = self._prime_db.get(bitlen=self._p_bitlen,
                                       primetype=self._primetype)
                q_generator = self._select_q(p)
            if q is None:
                q = next(q_generator)
            if self._args.gcd_n_phi_n:
                # q = (2 * r * p) + 1
                r = q
                q = 2 * r * p + 1
                if not NumberTheory.is_probable_prime(q):
                    q = None
                    continue

            # Always make p the smaller factor
            if p > q:
                (p, q) = (q, p)

            n = p * q
            if self._args.public_exponent == -1:
                e = random.randint(2, n - 1)
            else:
                e = self._args.public_exponent

            if self._args.carmichael_totient:
                totient = NumberTheory.lcm(p - 1, q - 1)
            else:
                totient = (p - 1) * (q - 1)
            gcd = NumberTheory.gcd(totient, e)
            if self._args.accept_unusable_key or (gcd == 1):
                break
            else:
                # Pair (phi(n), e) wasn't acceptable.
                self._log.debug("gcd(totient, e) was %d, retrying.", gcd)
                if self._args.public_exponent != -1:
                    # Public exponent e is fixed, need to choose another q.
                    if p.bit_length() == q.bit_length():
                        # Can re-use q as next p
                        (p, q) = (q, None)
                        q_generator = self._select_q(p)
                    else:
                        # When they differ in length, need to re-choose both values
                        (p, q) = (None, None)

        rsa_keypair = RSAPrivateKey.create(
            p=p,
            q=q,
            e=e,
            swap_e_d=self._args.switch_e_d,
            valid_only=not self._args.accept_unusable_key,
            carmichael_totient=self._args.carmichael_totient)
        rsa_keypair.write_pemfile(self._args.outfile)
        if self._args.verbose >= 1:
            diff = q - p
            print("Generated %d bit RSA key:" % (rsa_keypair.n.bit_length()))
            print("p = 0x%x" % (rsa_keypair.p))
            if not self._args.gcd_n_phi_n:
                print("q = 0x%x" % (rsa_keypair.q))
            else:
                print("q = 2 * r * p + 1 = 0x%x" % (rsa_keypair.q))
                print("r = 0x%x" % (r))

            print("phi(n) = 0x%x" % (rsa_keypair.phi_n))
            print("lambda(n) = 0x%x" % (rsa_keypair.lambda_n))
            print("phi(n) / lambda(n) = gcd(p - 1, q - 1) = %d" %
                  (rsa_keypair.phi_n // rsa_keypair.lambda_n))
            gcd_n_phin = NumberTheory.gcd(rsa_keypair.n, rsa_keypair.phi_n)
            if gcd_n_phin == rsa_keypair.p:
                print("gcd(n, phi(n)) = p")
            else:
                print("gcd(n, phi(n)) = 0x%x" % (gcd_n_phin))
            if self._args.close_q:
                print("q - p = %d (%d bit)" % (diff, diff.bit_length()))
            print("n = 0x%x" % (rsa_keypair.n))
            print("d = 0x%x" % (rsa_keypair.d))
            print("e = 0x%x" % (rsa_keypair.e))
예제 #7
0
    def analyze_n(self, n):
        judgements = SecurityJudgements()

        if n < 0:
            judgements += SecurityJudgement(
                JudgementCode.X509Cert_PublicKey_RSA_Modulus_Negative,
                "Modulus uses incorrect encoding, representation is a negative integer.",
                commonness=Commonness.HIGHLY_UNUSUAL,
                compatibility=Compatibility.STANDARDS_DEVIATION)

            # Fix up n so it's a positive integer for the rest of the tests
            bitlen = (n.bit_length() + 7) // 8 * 8
            mask = (1 << bitlen) - 1
            n = n & mask
        elif n == 0:
            judgements += SecurityJudgement(
                JudgementCode.X509Cert_PublicKey_RSA_Modulus_Zero,
                "Modulus is zero, this is definitely a broken RSA public key.",
                bits=0,
                commonness=Commonness.HIGHLY_UNUSUAL,
                compatibility=Compatibility.STANDARDS_DEVIATION)
        elif n == 1:
            judgements += SecurityJudgement(
                JudgementCode.X509Cert_PublicKey_RSA_Modulus_One,
                "Modulus is one, this is definitely a broken RSA public key.",
                bits=0,
                commonness=Commonness.HIGHLY_UNUSUAL,
                compatibility=Compatibility.STANDARDS_DEVIATION)

        if self._test_probable_prime:
            if NumberTheory.is_probable_prime(n):
                judgements += SecurityJudgement(
                    JudgementCode.X509Cert_PublicKey_RSA_Modulus_Prime,
                    "Modulus is prime, not a compound integer as we would expect for RSA.",
                    bits=0)

        if self._pollards_rho_iterations > 0:
            small_factor = NumberTheory.pollard_rho(
                n, max_iterations=self._pollards_rho_iterations)
            if small_factor is not None:
                judgements += SecurityJudgement(
                    JudgementCode.X509Cert_PublicKey_RSA_Modulus_Factorable,
                    "Modulus has small factor (%d) and is therefore trivially factorable."
                    % (small_factor),
                    bits=0)

        match = ModulusDB().find(n)
        if match is not None:
            judgements += SecurityJudgement(
                JudgementCode.
                X509Cert_PublicKey_RSA_Modulus_FactorizationKnown,
                "Modulus is known to be compromised: %s" % (match.text),
                bits=0)

        hweight_analysis = NumberTheory.hamming_weight_analysis(n)
        if (hweight_analysis
                is not None) and (not hweight_analysis.plausibly_random):
            judgements += SecurityJudgement(
                JudgementCode.X509Cert_PublicKey_RSA_Modulus_BitBiasPresent,
                "Modulus does not appear to be random. Expected a Hamming weight between %d and %d for a %d bit modulus, but found Hamming weight %d."
                % (hweight_analysis.rnd_min_hweight,
                   hweight_analysis.rnd_max_hweight, hweight_analysis.bitlen,
                   hweight_analysis.hweight),
                commonness=Commonness.HIGHLY_UNUSUAL)

        # We estimate the complexity of factoring the modulus by the asymptotic
        # complexity of the GNFS.
        bits_security = NumberTheory.asymtotic_complexity_gnfs_bits(n)
        judgements += self.algorithm("bits").analyze(
            JudgementCode.X509Cert_PublicKey_RSA_Modulus_LengthInBits,
            bits_security)

        return judgements
예제 #8
0
    def analyze(self, pubkey):
        judgements = SecurityJudgements()

        L = pubkey.p.bit_length()
        N = pubkey.q.bit_length()

        if not NumberTheory.is_probable_prime(pubkey.p):
            standard = LiteratureReference(
                quote="p: a prime modulus",
                sect="4.1",
                author="National Institute of Standards and Technology",
                title="FIPS PUB 186-4: Digital Signature Standard (DSS)",
                year=2013,
                month=7,
                doi="10.6028/NIST.FIPS.186-4")
            judgements += SecurityJudgement(
                JudgementCode.X509Cert_PublicKey_DSA_Parameters_P_NotPrime,
                "DSA parameter p is not prime.",
                commonness=Commonness.HIGHLY_UNUSUAL,
                compatibility=Compatibility.STANDARDS_DEVIATION,
                bits=0,
                standard=standard)

        if not NumberTheory.is_probable_prime(pubkey.q):
            standard = LiteratureReference(
                quote="q: a prime divisor of (p - 1)",
                sect="4.1",
                author="National Institute of Standards and Technology",
                title="FIPS PUB 186-4: Digital Signature Standard (DSS)",
                year=2013,
                month=7,
                doi="10.6028/NIST.FIPS.186-4")
            judgements += SecurityJudgement(
                JudgementCode.X509Cert_PublicKey_DSA_Parameters_Q_NotPrime,
                "DSA parameter q is not prime.",
                commonness=Commonness.HIGHLY_UNUSUAL,
                compatibility=Compatibility.STANDARDS_DEVIATION,
                bits=0,
                standard=standard)

        if ((pubkey.p - 1) % pubkey.q) != 0:
            standard = LiteratureReference(
                quote="q: a prime divisor of (p - 1)",
                sect="4.1",
                author="National Institute of Standards and Technology",
                title="FIPS PUB 186-4: Digital Signature Standard (DSS)",
                year=2013,
                month=7,
                doi="10.6028/NIST.FIPS.186-4")
            judgements += SecurityJudgement(
                JudgementCode.
                X509Cert_PublicKey_DSA_Parameters_Q_NoDivisorOfP1,
                "DSA parameter q is not a divisor of (p - 1).",
                commonness=Commonness.HIGHLY_UNUSUAL,
                compatibility=Compatibility.STANDARDS_DEVIATION,
                bits=0,
                standard=standard)

        if pow(pubkey.g, pubkey.q, pubkey.p) != 1:
            judgements += SecurityJudgement(
                JudgementCode.X509Cert_PublicKey_DSA_Parameters_G_Invalid,
                "DSA parameter g is not valid. In particular, g^q mod p != 1.",
                commonness=Commonness.HIGHLY_UNUSUAL,
                bits=0)

        if (pubkey.g <= 1) or (pubkey.g >= pubkey.p):
            standard = LiteratureReference(
                quote=
                "g: a generator of a subgroup of order q in the multiplicative group of GF(p), such that 1 < g < p",
                sect="4.1",
                author="National Institute of Standards and Technology",
                title="FIPS PUB 186-4: Digital Signature Standard (DSS)",
                year=2013,
                month=7,
                doi="10.6028/NIST.FIPS.186-4")
            judgements += SecurityJudgement(
                JudgementCode.X509Cert_PublicKey_DSA_Parameters_G_InvalidRange,
                "DSA parameter g is not inside the valid range (1 < g < p).",
                commonness=Commonness.HIGHLY_UNUSUAL,
                compatibility=Compatibility.STANDARDS_DEVIATION,
                bits=0,
                standard=standard)

        hweight_analysis = NumberTheory.hamming_weight_analysis(pubkey.p)
        if not hweight_analysis.plausibly_random:
            judgements += SecurityJudgement(
                JudgementCode.
                X509Cert_PublicKey_DSA_Parameters_P_BitBiasPresent,
                "Hamming weight of DSA prime p is %d at bitlength %d, but expected a weight between %d and %d when randomly chosen; this is likely not coincidential."
                % (hweight_analysis.hweight, hweight_analysis.bitlen,
                   hweight_analysis.rnd_min_hweight,
                   hweight_analysis.rnd_max_hweight),
                commonness=Commonness.HIGHLY_UNUSUAL)

        hweight_analysis = NumberTheory.hamming_weight_analysis(pubkey.q)
        if not hweight_analysis.plausibly_random:
            judgements += SecurityJudgement(
                JudgementCode.
                X509Cert_PublicKey_DSA_Parameters_Q_BitBiasPresent,
                "Hamming weight of DSA prime q is %d at bitlength %d, but expected a weight between %d and %d when randomly chosen; this is likely not coincidential."
                % (hweight_analysis.hweight, hweight_analysis.bitlen,
                   hweight_analysis.rnd_min_hweight,
                   hweight_analysis.rnd_max_hweight),
                commonness=Commonness.HIGHLY_UNUSUAL)

        if (L in self._TYPICAL_L_N_VALUES) and (
                N in self._TYPICAL_L_N_VALUES[L]):
            # Typical
            judgements += SecurityJudgement(
                JudgementCode.X509Cert_PublicKey_DSA_L_N_Common,
                "DSA parameter values L/N (%d/%d) are common." % (L, N),
                commonness=Commonness.COMMON)
        else:
            # Non-typical
            judgements += SecurityJudgement(
                JudgementCode.X509Cert_PublicKey_DSA_L_N_Uncommon,
                "DSA parameter values L/N (%d/%d) are uncommon." % (L, N),
                commonness=Commonness.UNUSUAL)

        L_strength_bits = NumberTheory.asymtotic_complexity_gnfs_bits(pubkey.p)
        N_strength_bits = math.floor(N / 2)
        bits_security = min(L_strength_bits, N_strength_bits)
        judgements += self.algorithm("bits").analyze(
            JudgementCode.X509Cert_PublicKey_DSA_L_N, bits_security)

        result = {
            "cryptosystem": "dsa",
            "specific": {
                "L": L,
                "N": N,
            },
            "security": judgements,
        }

        if self._analysis_options.include_raw_data:
            result["specific"]["p"] = {
                "value": pubkey.p,
            }
            result["specific"]["q"] = {
                "value": pubkey.q,
            }
            result["specific"]["g"] = {
                "value": pubkey.g,
            }
            result["specific"]["pubkey"] = {
                "value": pubkey.pubkey,
            }
        return result