Skip to content

Instantly share code, notes, and snippets.

@akgood
Last active October 16, 2017 21:52
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save akgood/99dc8b3c42a62bc943fcbe4a780b7a2c to your computer and use it in GitHub Desktop.
Save akgood/99dc8b3c42a62bc943fcbe4a780b7a2c to your computer and use it in GitHub Desktop.
Some quick hacks to test your SSH pubkeys for the "ROCA" vuln (https://crocs.fi.muni.cz/public/papers/rsa_ccs17) without having to install any third-party python modules...
#!/usr/bin/env python
# run me as e.g. "ssh-add -L | python2.7 detect_roca_sshkeys.py"
import argparse
import base64
import csv
import re
import struct
import sys
def parse_mpint(data):
""" parse OpenSSH "mpint" from https://tools.ietf.org/html/rfc4251,
that is, an arbitrary-precision 2s complement integer"""
# interpret the first byte as a signed char
msbyte = struct.unpack('b', data[0])[0]
# interpret the rest of the bytes as an arbitrary-length unsigned
# value. If data were unsigned, we could've just done this
data = data[1:]
if data:
val = int(data.encode('hex'), 16)
else:
val = 0
# add the most significant byte back on
val |= msbyte << (8 * len(data))
return val
def parse_ssh_key_line(line):
"""return tuple of (key algorithm, key data, comment)"""
fields = line.strip().split()
# skip "command=" and whatever else
for i, field in enumerate(fields):
if field in ('ssh-rsa', 'ssh-dss'):
fields = fields[i:]
break
return (fields[0], fields[1].decode('base64'), ' '.join(fields[2:]))
def parse_ssh_rsa_key(keydata):
"""return tuple of (public exponent, modulus) as integers"""
keydata_fields = []
while keydata:
length = struct.unpack("!L", keydata[:4])[0]
keydata = keydata[4:]
keydata_fields.append(keydata[:length])
keydata = keydata[length:]
assert len(keydata_fields) == 3
assert(keydata_fields[0] == 'ssh-rsa')
return [parse_mpint(x) for x in keydata_fields[1:]]
class SSHKeyLine(object):
roca_primes = [
3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71,
73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149,
151, 157, 163, 167]
roca_prints = [
6, 30, 126, 1026, 5658, 107286, 199410, 8388606, 536870910, 2147483646,
67109890, 2199023255550, 8796093022206, 140737488355326,
5310023542746834, 576460752303423486, 1455791217086302986,
147573952589676412926, 20052041432995567486, 6041388139249378920330,
207530445072488465666,
9671406556917033397649406,
618970019642690137449562110,
79228162521181866724264247298,
2535301200456458802993406410750,
1760368345969468176824550810518,
50079290986288516948354744811034,
473022961816146413042658758988474,
10384593717069655257060992658440190,
144390480366845522447407333004847678774,
2722258935367507707706996859454145691646,
174224571863520493293247799005065324265470,
696898287454081973172991196020261297061886,
713623846352979940529142984724747568191373310,
1800793591454480341970779146165214289059119882,
126304807362733370595828809000324029340048915994,
11692013098647223345629478661730264157247460343806,
187072209578355573530071658587684226515959365500926]
def is_roca_key(self):
if self.m is None:
# not ssh-rsa
return False
for i in range(0, len(self.roca_primes)):
if (1 << (self.m % self.roca_primes[i])) & self.roca_prints[i] == 0:
return False
return True
def __init__(self, line):
self.line = line
(self.algorithm, self.keydata, self.comment) = parse_ssh_key_line(line)
self.e, self.m = None, None
if self.algorithm == 'ssh-rsa':
self.e, self.m = parse_ssh_rsa_key(self.keydata)
def main():
parser = argparse.ArgumentParser()
parser.add_argument(
'--file', type=argparse.FileType('r'), default=sys.stdin)
args = parser.parse_args()
roca_count = 0
for count, line in enumerate(args.file):
line = line.strip()
k = SSHKeyLine(line)
if k.is_roca_key():
roca_count += 1
attrs = {
'line': line,
'public_exponent': k.e,
'modulus': k.m,
'ROCA': True}
print attrs
print "Detected {} ROCA-impacted key(s) out of {} total".format(roca_count, count+1)
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment