Coverage for /pythoncovmergedfiles/medio/medio/src/paramiko/paramiko/primes.py: 15%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com>
2#
3# This file is part of paramiko.
4#
5# Paramiko is free software; you can redistribute it and/or modify it under the
6# terms of the GNU Lesser General Public License as published by the Free
7# Software Foundation; either version 2.1 of the License, or (at your option)
8# any later version.
9#
10# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
11# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
13# details.
14#
15# You should have received a copy of the GNU Lesser General Public License
16# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
17# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19"""
20Utility functions for dealing with primes.
21"""
23import os
25from paramiko import util
26from paramiko.common import byte_mask
27from paramiko.ssh_exception import SSHException
30def _roll_random(n):
31 """returns a random # from 0 to N-1"""
32 bits = util.bit_length(n - 1)
33 byte_count = (bits + 7) // 8
34 hbyte_mask = pow(2, bits % 8) - 1
36 # so here's the plan:
37 # we fetch as many random bits as we'd need to fit N-1, and if the
38 # generated number is >= N, we try again. in the worst case (N-1 is a
39 # power of 2), we have slightly better than 50% odds of getting one that
40 # fits, so i can't guarantee that this loop will ever finish, but the odds
41 # of it looping forever should be infinitesimal.
42 while True:
43 x = os.urandom(byte_count)
44 if hbyte_mask > 0:
45 x = byte_mask(x[0], hbyte_mask) + x[1:]
46 num = util.inflate_long(x, 1)
47 if num < n:
48 break
49 return num
52class ModulusPack:
53 """
54 convenience object for holding the contents of the /etc/ssh/moduli file,
55 on systems that have such a file.
56 """
58 def __init__(self):
59 # pack is a hash of: bits -> [ (generator, modulus) ... ]
60 self.pack = {}
61 self.discarded = []
63 def _parse_modulus(self, line):
64 (
65 timestamp,
66 mod_type,
67 tests,
68 tries,
69 size,
70 generator,
71 modulus,
72 ) = line.split()
73 mod_type = int(mod_type)
74 tests = int(tests)
75 tries = int(tries)
76 size = int(size)
77 generator = int(generator)
78 modulus = int(modulus, 16)
80 # weed out primes that aren't at least:
81 # type 2 (meets basic structural requirements)
82 # test 4 (more than just a small-prime sieve)
83 # tries < 100 if test & 4 (at least 100 tries of miller-rabin)
84 if (
85 mod_type < 2
86 or tests < 4
87 or (tests & 4 and tests < 8 and tries < 100)
88 ):
89 self.discarded.append(
90 (modulus, "does not meet basic requirements")
91 )
92 return
93 if generator == 0:
94 generator = 2
96 # there's a bug in the ssh "moduli" file (yeah, i know: shock! dismay!
97 # call cnn!) where it understates the bit lengths of these primes by 1.
98 # this is okay.
99 bl = util.bit_length(modulus)
100 if (bl != size) and (bl != size + 1):
101 self.discarded.append(
102 (modulus, "incorrectly reported bit length {}".format(size))
103 )
104 return
105 if bl not in self.pack:
106 self.pack[bl] = []
107 self.pack[bl].append((generator, modulus))
109 def read_file(self, filename):
110 """
111 :raises IOError: passed from any file operations that fail.
112 """
113 self.pack = {}
114 with open(filename, "r") as f:
115 for line in f:
116 line = line.strip()
117 if (len(line) == 0) or (line[0] == "#"):
118 continue
119 try:
120 self._parse_modulus(line)
121 except:
122 continue
124 def get_modulus(self, min, prefer, max):
125 bitsizes = sorted(self.pack.keys())
126 if len(bitsizes) == 0:
127 raise SSHException("no moduli available")
128 good = -1
129 # find nearest bitsize >= preferred
130 for b in bitsizes:
131 if (b >= prefer) and (b <= max) and (b < good or good == -1):
132 good = b
133 # if that failed, find greatest bitsize >= min
134 if good == -1:
135 for b in bitsizes:
136 if (b >= min) and (b <= max) and (b > good):
137 good = b
138 if good == -1:
139 # their entire (min, max) range has no intersection with our range.
140 # if their range is below ours, pick the smallest. otherwise pick
141 # the largest. it'll be out of their range requirement either way,
142 # but we'll be sending them the closest one we have.
143 good = bitsizes[0]
144 if min > good:
145 good = bitsizes[-1]
146 # now pick a random modulus of this bitsize
147 n = _roll_random(len(self.pack[good]))
148 return self.pack[good][n]