1# Copyright (C) 2011 Jeff Forcier <jeff@bitprophet.org> 2# 3# This file is part of ssh. 4# 5# 'ssh' 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# 'ssh' is distrubuted 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 'ssh'; if not, write to the Free Software Foundation, Inc., 17# 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA. 18 19""" 20Some unit tests for public/private key objects. 21""" 22 23from binascii import hexlify, unhexlify 24import StringIO 25import unittest 26from ssh import RSAKey, DSSKey, Message, util 27from ssh.common import rng 28 29# from openssh's ssh-keygen 30PUB_RSA = 'ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEA049W6geFpmsljTwfvI1UmKWWJPNFI74+vNKTk4dmzkQY2yAMs6FhlvhlI8ysU4oj71ZsRYMecHbBbxdN79+JRFVYTKaLqjwGENeTd+yv4q+V2PvZv3fLnzApI3l7EJCqhWwJUHJ1jAkZzqDx0tyOL4uoZpww3nmE0kb3y21tH4c=' 31PUB_DSS = 'ssh-dss AAAAB3NzaC1kc3MAAACBAOeBpgNnfRzr/twmAQRu2XwWAp3CFtrVnug6s6fgwj/oLjYbVtjAy6pl/h0EKCWx2rf1IetyNsTxWrniA9I6HeDj65X1FyDkg6g8tvCnaNB8Xp/UUhuzHuGsMIipRxBxw9LF608EqZcj1E3ytktoW5B5OcjrkEoz3xG7C+rpIjYvAAAAFQDwz4UnmsGiSNu5iqjn3uTzwUpshwAAAIEAkxfFeY8P2wZpDjX0MimZl5wkoFQDL25cPzGBuB4OnB8NoUk/yjAHIIpEShw8V+LzouMK5CTJQo5+Ngw3qIch/WgRmMHy4kBq1SsXMjQCte1So6HBMvBPIW5SiMTmjCfZZiw4AYHK+B/JaOwaG9yRg2Ejg4Ok10+XFDxlqZo8Y+wAAACARmR7CCPjodxASvRbIyzaVpZoJ/Z6x7dAumV+ysrV1BVYd0lYukmnjO1kKBWApqpH1ve9XDQYN8zgxM4b16L21kpoWQnZtXrY3GZ4/it9kUgyB7+NwacIBlXa8cMDL7Q/69o0d54U0X/NeX5QxuYR6OMJlrkQB7oiW/P/1mwjQgE=' 32FINGER_RSA = '1024 60:73:38:44:cb:51:86:65:7f:de:da:a2:2b:5a:57:d5' 33FINGER_DSS = '1024 44:78:f0:b9:a2:3c:c5:18:20:09:ff:75:5b:c1:d2:6c' 34SIGNED_RSA = '20:d7:8a:31:21:cb:f7:92:12:f2:a4:89:37:f5:78:af:e6:16:b6:25:b9:97:3d:a2:cd:5f:ca:20:21:73:4c:ad:34:73:8f:20:77:28:e2:94:15:08:d8:91:40:7a:85:83:bf:18:37:95:dc:54:1a:9b:88:29:6c:73:ca:38:b4:04:f1:56:b9:f2:42:9d:52:1b:29:29:b4:4f:fd:c9:2d:af:47:d2:40:76:30:f3:63:45:0c:d9:1d:43:86:0f:1c:70:e2:93:12:34:f3:ac:c5:0a:2f:14:50:66:59:f1:88:ee:c1:4a:e9:d1:9c:4e:46:f0:0e:47:6f:38:74:f1:44:a8' 35 36RSA_PRIVATE_OUT = """\ 37-----BEGIN RSA PRIVATE KEY----- 38MIICXAIBAAKCAIEA049W6geFpmsljTwfvI1UmKWWJPNFI74+vNKTk4dmzkQY2yAM 39s6FhlvhlI8ysU4oj71ZsRYMecHbBbxdN79+JRFVYTKaLqjwGENeTd+yv4q+V2PvZ 40v3fLnzApI3l7EJCqhWwJUHJ1jAkZzqDx0tyOL4uoZpww3nmE0kb3y21tH4cCASMC 41ggCAEiI6plhqipt4P05L3PYr0pHZq2VPEbE4k9eI/gRKo/c1VJxY3DJnc1cenKsk 42trQRtW3OxCEufqsX5PNec6VyKkW+Ox6beJjMKm4KF8ZDpKi9Nw6MdX3P6Gele9D9 43+ieyhVFljrnAqcXsgChTBOYlL2imqCs3qRGAJ3cMBIAx3VsCQQD3pIFVYW398kE0 44n0e1icEpkbDRV4c5iZVhu8xKy2yyfy6f6lClSb2+Ub9uns7F3+b5v0pYSHbE9+/r 45OpRq83AfAkEA2rMZlr8SnMXgnyka2LuggA9QgMYy18hyao1dUxySubNDa9N+q2QR 46mwDisTUgRFHKIlDHoQmzPbXAmYZX1YlDmQJBAPCRLS5epV0XOAc7pL762OaNhzHC 47veAfQKgVhKBt105PqaKpGyQ5AXcNlWQlPeTK4GBTbMrKDPna6RBkyrEJvV8CQBK+ 485O+p+kfztCrmRCE0p1tvBuZ3Y3GU1ptrM+KNa6mEZN1bRV8l1Z+SXJLYqv6Kquz/ 49nBUeFq2Em3rfoSDugiMCQDyG3cxD5dKX3IgkhLyBWls/FLDk4x/DQ+NUTu0F1Cu6 50JJye+5ARLkL0EweMXf0tmIYfWItDLsWB0fKg/56h0js= 51-----END RSA PRIVATE KEY----- 52""" 53 54DSS_PRIVATE_OUT = """\ 55-----BEGIN DSA PRIVATE KEY----- 56MIIBvgIBAAKCAIEA54GmA2d9HOv+3CYBBG7ZfBYCncIW2tWe6Dqzp+DCP+guNhtW 572MDLqmX+HQQoJbHat/Uh63I2xPFaueID0jod4OPrlfUXIOSDqDy28Kdo0Hxen9RS 58G7Me4awwiKlHEHHD0sXrTwSplyPUTfK2S2hbkHk5yOuQSjPfEbsL6ukiNi8CFQDw 59z4UnmsGiSNu5iqjn3uTzwUpshwKCAIEAkxfFeY8P2wZpDjX0MimZl5wkoFQDL25c 60PzGBuB4OnB8NoUk/yjAHIIpEShw8V+LzouMK5CTJQo5+Ngw3qIch/WgRmMHy4kBq 611SsXMjQCte1So6HBMvBPIW5SiMTmjCfZZiw4AYHK+B/JaOwaG9yRg2Ejg4Ok10+X 62FDxlqZo8Y+wCggCARmR7CCPjodxASvRbIyzaVpZoJ/Z6x7dAumV+ysrV1BVYd0lY 63ukmnjO1kKBWApqpH1ve9XDQYN8zgxM4b16L21kpoWQnZtXrY3GZ4/it9kUgyB7+N 64wacIBlXa8cMDL7Q/69o0d54U0X/NeX5QxuYR6OMJlrkQB7oiW/P/1mwjQgECFGI9 65QPSch9pT9XHqn+1rZ4bK+QGA 66-----END DSA PRIVATE KEY----- 67""" 68 69 70class KeyTest (unittest.TestCase): 71 72 def setUp(self): 73 pass 74 75 def tearDown(self): 76 pass 77 78 def test_1_generate_key_bytes(self): 79 from Crypto.Hash import MD5 80 key = util.generate_key_bytes(MD5, '\x01\x02\x03\x04', 'happy birthday', 30) 81 exp = unhexlify('61E1F272F4C1C4561586BD322498C0E924672780F47BB37DDA7D54019E64') 82 self.assertEquals(exp, key) 83 84 def test_2_load_rsa(self): 85 key = RSAKey.from_private_key_file('tests/test_rsa.key') 86 self.assertEquals('ssh-rsa', key.get_name()) 87 exp_rsa = FINGER_RSA.split()[1].replace(':', '') 88 my_rsa = hexlify(key.get_fingerprint()) 89 self.assertEquals(exp_rsa, my_rsa) 90 self.assertEquals(PUB_RSA.split()[1], key.get_base64()) 91 self.assertEquals(1024, key.get_bits()) 92 93 s = StringIO.StringIO() 94 key.write_private_key(s) 95 self.assertEquals(RSA_PRIVATE_OUT, s.getvalue()) 96 s.seek(0) 97 key2 = RSAKey.from_private_key(s) 98 self.assertEquals(key, key2) 99 100 def test_3_load_rsa_password(self): 101 key = RSAKey.from_private_key_file('tests/test_rsa_password.key', 'television') 102 self.assertEquals('ssh-rsa', key.get_name()) 103 exp_rsa = FINGER_RSA.split()[1].replace(':', '') 104 my_rsa = hexlify(key.get_fingerprint()) 105 self.assertEquals(exp_rsa, my_rsa) 106 self.assertEquals(PUB_RSA.split()[1], key.get_base64()) 107 self.assertEquals(1024, key.get_bits()) 108 109 def test_4_load_dss(self): 110 key = DSSKey.from_private_key_file('tests/test_dss.key') 111 self.assertEquals('ssh-dss', key.get_name()) 112 exp_dss = FINGER_DSS.split()[1].replace(':', '') 113 my_dss = hexlify(key.get_fingerprint()) 114 self.assertEquals(exp_dss, my_dss) 115 self.assertEquals(PUB_DSS.split()[1], key.get_base64()) 116 self.assertEquals(1024, key.get_bits()) 117 118 s = StringIO.StringIO() 119 key.write_private_key(s) 120 self.assertEquals(DSS_PRIVATE_OUT, s.getvalue()) 121 s.seek(0) 122 key2 = DSSKey.from_private_key(s) 123 self.assertEquals(key, key2) 124 125 def test_5_load_dss_password(self): 126 key = DSSKey.from_private_key_file('tests/test_dss_password.key', 'television') 127 self.assertEquals('ssh-dss', key.get_name()) 128 exp_dss = FINGER_DSS.split()[1].replace(':', '') 129 my_dss = hexlify(key.get_fingerprint()) 130 self.assertEquals(exp_dss, my_dss) 131 self.assertEquals(PUB_DSS.split()[1], key.get_base64()) 132 self.assertEquals(1024, key.get_bits()) 133 134 def test_6_compare_rsa(self): 135 # verify that the private & public keys compare equal 136 key = RSAKey.from_private_key_file('tests/test_rsa.key') 137 self.assertEquals(key, key) 138 pub = RSAKey(data=str(key)) 139 self.assert_(key.can_sign()) 140 self.assert_(not pub.can_sign()) 141 self.assertEquals(key, pub) 142 143 def test_7_compare_dss(self): 144 # verify that the private & public keys compare equal 145 key = DSSKey.from_private_key_file('tests/test_dss.key') 146 self.assertEquals(key, key) 147 pub = DSSKey(data=str(key)) 148 self.assert_(key.can_sign()) 149 self.assert_(not pub.can_sign()) 150 self.assertEquals(key, pub) 151 152 def test_8_sign_rsa(self): 153 # verify that the rsa private key can sign and verify 154 key = RSAKey.from_private_key_file('tests/test_rsa.key') 155 msg = key.sign_ssh_data(rng, 'ice weasels') 156 self.assert_(type(msg) is Message) 157 msg.rewind() 158 self.assertEquals('ssh-rsa', msg.get_string()) 159 sig = ''.join([chr(int(x, 16)) for x in SIGNED_RSA.split(':')]) 160 self.assertEquals(sig, msg.get_string()) 161 msg.rewind() 162 pub = RSAKey(data=str(key)) 163 self.assert_(pub.verify_ssh_sig('ice weasels', msg)) 164 165 def test_9_sign_dss(self): 166 # verify that the dss private key can sign and verify 167 key = DSSKey.from_private_key_file('tests/test_dss.key') 168 msg = key.sign_ssh_data(rng, 'ice weasels') 169 self.assert_(type(msg) is Message) 170 msg.rewind() 171 self.assertEquals('ssh-dss', msg.get_string()) 172 # can't do the same test as we do for RSA, because DSS signatures 173 # are usually different each time. but we can test verification 174 # anyway so it's ok. 175 self.assertEquals(40, len(msg.get_string())) 176 msg.rewind() 177 pub = DSSKey(data=str(key)) 178 self.assert_(pub.verify_ssh_sig('ice weasels', msg)) 179 180 def test_A_generate_rsa(self): 181 key = RSAKey.generate(1024) 182 msg = key.sign_ssh_data(rng, 'jerri blank') 183 msg.rewind() 184 self.assert_(key.verify_ssh_sig('jerri blank', msg)) 185 186 def test_B_generate_dss(self): 187 key = DSSKey.generate(1024) 188 msg = key.sign_ssh_data(rng, 'jerri blank') 189 msg.rewind() 190 self.assert_(key.verify_ssh_sig('jerri blank', msg)) 191