def verify_ssh_sig(self, data, msg): if msg.get_string() != 'ssh-rsa': return False sig = util.inflate_long(msg.get_string(), True) # verify the signature by SHA'ing the data and encrypting it using the # public key. some wackiness ensues where we "pkcs1imify" the 20-byte # hash into a string as long as the RSA key. hash_obj = util.inflate_long(self._pkcs1imify(SHA.new(data).digest()), True) rsa = RSA.construct((long(self.n), long(self.e))) return rsa.verify(hash_obj, (sig, ))
def verify_ssh_sig(self, data, msg): if len(str(msg)) == 40: # spies.com bug: signature has no header sig = str(msg) else: kind = msg.get_string() if kind != 'ssh-dss': return 0 sig = msg.get_string() # pull out (r, s) which are NOT encoded as mpints sigR = util.inflate_long(sig[:20], 1) sigS = util.inflate_long(sig[20:], 1) sigM = util.inflate_long(SHA.new(data).digest(), 1) dss = DSA.construct((long(self.y), long(self.g), long(self.p), long(self.q))) return dss.verify(sigM, (sigR, sigS))
def get_mpint(self): """ Fetch a long int (mpint) from the stream. @return: an arbitrary-length integer. @rtype: long """ return util.inflate_long(self.get_string())
def sign_ssh_data(self, rpool, data): digest = SHA.new(data).digest() dss = DSA.construct((long(self.y), long(self.g), long(self.p), long(self.q), long(self.x))) # generate a suitable k qsize = len(util.deflate_long(self.q, 0)) while True: k = util.inflate_long(rpool.get_bytes(qsize), 1) if (k > 2) and (k < self.q): break r, s = dss.sign(util.inflate_long(digest, 1), k) m = Message() m.add_string('ssh-dss') # apparently, in rare cases, r or s may be shorter than 20 bytes! rstr = util.deflate_long(r, 0) sstr = util.deflate_long(s, 0) if len(rstr) < 20: rstr = '\x00' * (20 - len(rstr)) + rstr if len(sstr) < 20: sstr = '\x00' * (20 - len(sstr)) + sstr m.add_string(rstr + sstr) return m
def _generate_x(self): # generate an "x" (1 < x < q), where q is (p-1)/2. # p is a 128-byte (1024-bit) number, where the first 64 bits are 1. # therefore q can be approximated as a 2^1023. we drop the subset of # potential x where the first 63 bits are 1, because some of those will be # larger than q (but this is a tiny tiny subset of potential x). while 1: self.transport.randpool.stir() x_bytes = self.transport.randpool.get_bytes(128) x_bytes = chr(ord(x_bytes[0]) & 0x7f) + x_bytes[1:] if (x_bytes[:8] != '\x7F\xFF\xFF\xFF\xFF\xFF\xFF\xFF') and \ (x_bytes[:8] != '\x00\x00\x00\x00\x00\x00\x00\x00'): break self.x = util.inflate_long(x_bytes)
def _generate_prime(bits, randpool): "primtive attempt at prime generation" hbyte_mask = pow(2, bits % 8) - 1 while True: # loop catches the case where we increment n into a higher bit-range x = randpool.get_bytes((bits + 7) // 8) if hbyte_mask > 0: x = chr(ord(x[0]) & hbyte_mask) + x[1:] n = util.inflate_long(x, 1) n |= 1 n |= (1 << (bits - 1)) while not number.isPrime(n): n += 2 if util.bit_length(n) == bits: break return n
def _generate_x(self): # generate an "x" (1 < x < (p-1)/2). q = (self.p - 1) // 2 qnorm = util.deflate_long(q, 0) qhbyte = ord(qnorm[0]) bytes = len(qnorm) qmask = 0xff while not (qhbyte & 0x80): qhbyte <<= 1 qmask >>= 1 while True: self.transport.randpool.stir() x_bytes = self.transport.randpool.get_bytes(bytes) x_bytes = chr(ord(x_bytes[0]) & qmask) + x_bytes[1:] x = util.inflate_long(x_bytes, 1) if (x > 1) and (x < q): break self.x = x
def _roll_random(rpool, n): "returns a random # from 0 to N-1" bits = util.bit_length(n - 1) bytes = (bits + 7) // 8 hbyte_mask = pow(2, bits % 8) - 1 # so here's the plan: # we fetch as many random bits as we'd need to fit N-1, and if the # generated number is >= N, we try again. in the worst case (N-1 is a # power of 2), we have slightly better than 50% odds of getting one that # fits, so i can't guarantee that this loop will ever finish, but the odds # of it looping forever should be infinitesimal. while True: x = rpool.get_bytes(bytes) if hbyte_mask > 0: x = chr(ord(x[0]) & hbyte_mask) + x[1:] num = util.inflate_long(x, 1) if num < n: break return num