Improve tool/
[gnuk/gnuk.git] / tool / gnuk_upgrade.py
1 #! /usr/bin/python
2
3 """
4 gnuk_upgrade.py - a tool to upgrade firmware of Gnuk Token
5
6 Copyright (C) 2012, 2015 Free Software Initiative of Japan
7 Author: NIIBE Yutaka <gniibe@fsij.org>
8
9 This file is a part of Gnuk, a GnuPG USB Token implementation.
10
11 Gnuk is free software: you can redistribute it and/or modify it
12 under the terms of the GNU General Public License as published by
13 the Free Software Foundation, either version 3 of the License, or
14 (at your option) any later version.
15
16 Gnuk is distributed in the hope that it will be useful, but WITHOUT
17 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
18 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
19 License for more details.
20
21 You should have received a copy of the GNU General Public License
22 along with this program.  If not, see <http://www.gnu.org/licenses/>.
23 """
24
25 from struct import *
26 import sys, time, os, binascii, string
27
28 # INPUT: binary files (regnual_image, upgrade_firmware_image)
29
30 # Assume only single CCID device is attached to computer, and it's Gnuk Token
31
32 import usb
33
34 from gnuk_token import *
35
36 def to_string(t):
37     result = ""
38     for c in t:
39         result += chr(c)
40     return result
41
42 from subprocess import check_output
43
44 SHA256_OID_PREFIX="3031300d060960864801650304020105000420"
45
46 # When user specify KEYGRIP, use it.  Or else, connect to SCD directly.
47 def gpg_sign(keygrip, hash):
48     if keygrip:
49         result = check_output(["gpg-connect-agent",
50                                "SIGKEY %s" % keygrip,
51                                "SETHASH --hash=sha256 %s" % hash,
52                                "PKSIGN --hash=sha256", "/bye"])
53     else:
54         result = check_output(["gpg-connect-agent",
55                                "SCD SETDATA " + SHA256_OID_PREFIX + hash,
56                                "SCD PKAUTH OPENPGP.3",
57                                "/bye"])
58     signed = ""
59     while True:
60         i = result.find('%')
61         if i < 0:
62             signed += result
63             break
64         hex_str = result[i+1:i+3]
65         signed += result[0:i]
66         signed += chr(int(hex_str,16))
67         result = result[i+3:]
68
69     if keygrip:
70         pos = signed.index("D (7:sig-val(3:rsa(1:s256:") + 26
71         signed = signed[pos:-7]
72     else:
73         pos = signed.index("D ") + 2
74         signed = signed[pos:-4]     # \nOK\n
75     if len(signed) != 256:
76         raise ValueError(binascii.hexlify(signed))
77     return signed
78
79 def main(keyno,keygrip, data_regnual, data_upgrade):
80     l = len(data_regnual)
81     if (l & 0x03) != 0:
82         data_regnual = data_regnual.ljust(l + 4 - (l & 0x03), chr(0))
83     crc32code = crc32(data_regnual)
84     print "CRC32: %04x\n" % crc32code
85     data_regnual += pack('<I', crc32code)
86     for (dev, config, intf) in gnuk_devices():
87         try:
88             icc = gnuk_token(dev, config, intf)
89             print "Device: ", dev.filename
90             print "Configuration: ", config.value
91             print "Interface: ", intf.interfaceNumber
92             break
93         except:
94             icc = None
95     if icc.icc_get_status() == 2:
96         raise ValueError("No ICC present")
97     elif icc.icc_get_status() == 1:
98         icc.icc_power_on()
99     icc.cmd_select_openpgp()
100     challenge = icc.cmd_get_challenge()
101     signed = gpg_sign(keygrip, binascii.hexlify(to_string(challenge)))
102     icc.cmd_external_authenticate(keyno, signed)
103     icc.stop_gnuk()
104     mem_info = icc.mem_info()
105     print "%08x:%08x" % mem_info
106     print "Downloading flash upgrade program..."
107     icc.download(mem_info[0], data_regnual)
108     print "Run flash upgrade program..."
109     icc.execute(mem_info[0] + len(data_regnual) - 4)
110     #
111     time.sleep(3)
112     icc.reset_device()
113     del icc
114     icc = None
115     #
116     print "Wait 3 seconds..."
117     time.sleep(3)
118     # Then, send upgrade program...
119     reg = None
120     for dev in gnuk_devices_by_vidpid():
121         try:
122             reg = regnual(dev)
123             print "Device: ", dev.filename
124             break
125         except:
126             pass
127     mem_info = reg.mem_info()
128     print "%08x:%08x" % mem_info
129     print "Downloading the program"
130     reg.download(mem_info[0], data_upgrade)
131     reg.protect()
132     reg.finish()
133     reg.reset_device()
134     return 0
135
136
137 if __name__ == '__main__':
138     keyno = 0
139     keygrip = None
140     if sys.argv[1] == '-k':
141         sys.argv.pop(1)
142         keygrip = sys.argv[1]
143         sys.argv.pop(1)
144     filename_regnual = sys.argv[1]
145     filename_upgrade = sys.argv[2]
146     f = open(filename_regnual)
147     data_regnual = f.read()
148     f.close()
149     print "%s: %d" % (filename_regnual, len(data_regnual))
150     f = open(filename_upgrade)
151     data_upgrade = f.read()
152     f.close()
153     print "%s: %d" % (filename_upgrade, len(data_upgrade))
154     main(keyno, keygrip, data_regnual, data_upgrade[4096:])