a little nicer wrt pep8
[sfa.git] / keyconvert / keyconvert.py
1 #!/usr/bin/env python3
2
3 import sys
4 import base64
5 import struct
6 import binascii
7 from M2Crypto import RSA, DSA, m2
8
9
10 ###### Workaround for bug in m2crypto-0.18 (on Fedora 8)
11 class RSA_pub_fix(RSA.RSA_pub):
12     def save_key_bio(self, bio, *args, **kw):
13         return self.save_pub_key_bio(bio)
14
15 def rsa_new_pub_key(couple):
16     (e, n)=couple
17     rsa = m2.rsa_new()
18     m2.rsa_set_e(rsa, e)
19     m2.rsa_set_n(rsa, n)
20     return RSA_pub_fix(rsa, 1)
21 ######
22 #rsa_new_pub_key = RSA.new_pub_key
23
24
25 def decode_key(fname):
26     """Convert base64 encoded openssh key to binary"""
27     contents = open(fname).read()
28     fields = contents.split()
29
30     in_key = False
31     for f in fields:
32         f = f.strip()
33         if f.startswith("ssh-"):
34             in_key = True
35             continue
36         elif in_key:
37             return base64.b64decode(f)
38
39     return None
40
41
42 # openssh binary key format
43 #
44 # a section:
45 # length = 4 bytes (32-bit big-endian integer)
46 # data = length bytes of string
47 #
48 # sections of the key ( for RSA )
49 # [key-type (in ASCII)] [public exponent (bignum)] [primes (bignum)]
50 #
51 # sections of the key ( for DSA )
52 # [key-type (in ASCII)] [p (bignum)] [q (bignum)] [g (bignum)] [y (bignum)]
53 #
54 # - baris
55 def read_key(key):
56
57     def read_length(key):
58         length = key[0:4]
59         length = struct.unpack(">l", length)[0]
60         return length, key
61
62     def read_values(key, count):
63         v = []
64         for i in range(count):
65             length, key = read_length(key)
66             size = 4 + length
67             v.append(key[:size])
68             key = key[size:]
69         return v
70
71     length, key = read_length(key)
72     key = key[4:]
73     key_type = key[:length]
74     key = key[length:]
75
76     if key_type == b"ssh-rsa":
77         # prepare parameters for RSA.new_pub_key
78         v = read_values(key, 2)
79         e, n = v[0], v[1]
80         return key_type, e, n
81
82     elif key_type == b"ssh-dss":
83         # prepare parameters for DSA.set_params
84         v = read_values(key, 4)
85         p, q, g, y = v[0], v[1], v[2], v[3]
86         return key_type, p, q, g, y
87
88
89 def convert(fin, fout):
90     key = decode_key(fin)
91     ret = read_key(key)
92     key_type = ret[0]
93
94     if key_type == b"ssh-rsa":
95         e, n = ret[1:]
96         rsa = rsa_new_pub_key((e, n))
97         rsa.save_pem(fout)
98
99     elif key_type == b"ssh-dss":
100         p, q, g, y = ret[1:]
101         dsa = DSA.set_params(p, q, g)
102         dsa.gen_key()
103         dsa.save_pub_key(fout)
104         # FIXME: This is wrong.
105         # M2Crypto doesn't allow us to set the public key parameter
106         raise Exception("DSA keys are not supported yet: M2Crypto doesn't allow us to set the public key parameter")
107
108
109 if __name__ == "__main__":
110     if len(sys.argv) != 3:
111         print("Usage: %s <input-file> <output-file>")
112         sys.exit(1)
113
114     fin = sys.argv[1]
115     fout = sys.argv[2]
116     convert(fin, fout)