upgrade_by_passwd.py
authorNIIBE Yutaka <gniibe@fsij.org>
Tue, 25 Dec 2012 05:47:08 +0000 (14:47 +0900)
committerNIIBE Yutaka <gniibe@fsij.org>
Tue, 25 Dec 2012 05:47:49 +0000 (14:47 +0900)
ChangeLog
NEWS
test/factory_upgrade.py [deleted file]
tool/rsa.py [new file with mode: 0644]
tool/rsa_example.key [new file with mode: 0644]
tool/upgrade_by_passwd.py [new file with mode: 0755]

index 5050e14..2dc97b3 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,6 +1,12 @@
-2012-12-19  Niibe Yutaka  <gniibe@fsij.org>
+2012-12-25  Niibe Yutaka  <gniibe@fsij.org>
+
+       * tool/rsa.py: New.
+
+       * tool/rsa_example.key: New.  Example RSA key information.
 
-       * test/factory_upgrade.py: New.
+       * tool/upgrade_by_passwd.py: New.
+
+2012-12-19  Niibe Yutaka  <gniibe@fsij.org>
 
        * src/Makefile.in (USE_OPT): -O3 and -Os (was: -O2).
 
diff --git a/NEWS b/NEWS
index da6293b..94dddc2 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -8,7 +8,7 @@ Gnuk NEWS - User visible changes
 Since the USB ID Repository suggests not including vendor name
 in product string, we changed the product string.
 
-** New tool (experimental): test/factory_upgrade.py
+** New tool (experimental): test/upgrade_by_passwd.py
 This is the tool to install new firmware to Gnuk Token, provided
 that it's just shipped from factory (and nothing changed).  It
 authenticate as admin by factory setting, register a public key
diff --git a/test/factory_upgrade.py b/test/factory_upgrade.py
deleted file mode 100755 (executable)
index 2234a92..0000000
+++ /dev/null
@@ -1,111 +0,0 @@
-#! /usr/bin/python
-
-"""
-factory_upgrade.py - a tool to install another firmware for Gnuk Token
-                     which is just shipped from factory
-
-Copyright (C) 2012 Free Software Initiative of Japan
-Author: NIIBE Yutaka <gniibe@fsij.org>
-
-This file is a part of Gnuk, a GnuPG USB Token implementation.
-
-Gnuk is free software: you can redistribute it and/or modify it
-under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-Gnuk is distributed in the hope that it will be useful, but WITHOUT
-ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
-License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program.  If not, see <http://www.gnu.org/licenses/>.
-"""
-
-from gnuk_token import *
-import sys, binascii, time, os
-
-DEFAULT_PW3 = "12345678"
-BY_ADMIN = 3
-
-KEYNO_FOR_AUTH=2 
-
-def main(passwd, data_regnual, data_upgrade):
-    l = len(data_regnual)
-    if (l & 0x03) != 0:
-        data_regnual = data_regnual.ljust(l + 4 - (l & 0x03), chr(0))
-    crc32code = crc32(data_regnual)
-    print "CRC32: %04x\n" % crc32code
-    data_regnual += pack('<I', crc32code)
-
-    rsa_raw_pubkey = rsa.integer_to_bytes_256(rsa.key[KEYNO_FOR_AUTH][7])
-
-    gnuk = get_gnuk_device()
-    gnuk.cmd_verify(BY_ADMIN, passwd)
-    gnuk.cmd_write_binary(1, rsa_raw_pubkey, False)
-
-    gnuk.cmd_select_openpgp()
-    challenge = gnuk.cmd_get_challenge()
-    digestinfo = binascii.unhexlify(SHA256_OID_PREFIX) + challenge
-    signed = rsa.compute_signature(KEYNO_FOR_AUTH, digestinfo)
-    signed_bytes = rsa.integer_to_bytes_256(signed)
-    gnuk.cmd_external_authenticate(signed_bytes)
-    gnuk.stop_gnuk()
-    mem_info = gnuk.mem_info()
-    print "%08x:%08x" % mem_info
-
-    print "Downloading flash upgrade program..."
-    gnuk.download(mem_info[0], data_regnual)
-    print "Run flash upgrade program..."
-    gnuk.execute(mem_info[0] + len(data_regnual) - 4)
-    #
-    time.sleep(3)
-    gnuk.reset_device()
-    del gnuk
-    gnuk = None
-    #
-    print "Wait 3 seconds..."
-    time.sleep(3)
-    # Then, send upgrade program...
-    reg = None
-    for dev in gnuk_devices_by_vidpid():
-        try:
-            reg = regnual(dev)
-            print "Device: ", dev.filename
-            break
-        except:
-            pass
-    mem_info = reg.mem_info()
-    print "%08x:%08x" % mem_info
-    print "Downloading the program"
-    reg.download(mem_info[0], data_upgrade)
-    reg.protect()
-    reg.finish()
-    reg.reset_device()
-    return 0
-
-if __name__ == '__main__':
-    if os.getcwd() != os.path.dirname(os.path.abspath(__file__)):
-        print "Please change working directory to: %s" % os.path.dirname(os.path.abspath(__file__))
-        exit(1)
-
-    import rsa_keys as rsa
-
-    passwd = DEFAULT_PW3
-    if len(sys.argv) > 1 and sys.argv[1] == '-p':
-        from getpass import getpass
-        passwd = getpass("Admin password: ")
-        sys.argv.pop(1)
-    filename_regnual = sys.argv[1]
-    filename_upgrade = sys.argv[2]
-    f = open(filename_regnual)
-    data_regnual = f.read()
-    f.close()
-    print "%s: %d" % (filename_regnual, len(data_regnual))
-    f = open(filename_upgrade)
-    data_upgrade = f.read()
-    f.close()
-    print "%s: %d" % (filename_upgrade, len(data_upgrade))
-    # First 4096-byte in data_upgrade is SYS, so, skip it.
-    main(passwd, data_regnual, data_upgrade[4096:])
diff --git a/tool/rsa.py b/tool/rsa.py
new file mode 100644 (file)
index 0000000..00371f9
--- /dev/null
@@ -0,0 +1,70 @@
+from binascii import hexlify, unhexlify
+import string
+from os import urandom
+
+def read_key_from_file(file):
+    f = open(file)
+    n_str = f.readline()[:-1]
+    e_str = f.readline()[:-1]
+    p_str = f.readline()[:-1]
+    q_str = f.readline()[:-1]
+    f.close()
+    e = int(e_str, 16)
+    p = int(p_str, 16)
+    q = int(q_str, 16)
+    n = int(n_str, 16)
+    if n != p * q:
+        raise ValueError("wrong key", p, q, n)
+    return (unhexlify(n_str), unhexlify(e_str), unhexlify(p_str), unhexlify(q_str), e, p, q, n)
+
+# egcd and modinv are from wikibooks
+# https://en.wikibooks.org/wiki/Algorithm_Implementation/Mathematics/Extended_Euclidean_algorithm
+
+def egcd(a, b):
+    if a == 0:
+        return (b, 0, 1)
+    else:
+        g, y, x = egcd(b % a, a)
+        return (g, x - (b // a) * y, y)
+
+def modinv(a, m):
+    g, x, y = egcd(a, m)
+    if g != 1:
+        raise Exception('modular inverse does not exist')
+    else:
+        return x % m
+
+def pkcs1_pad_for_sign(digestinfo):
+    byte_repr = '\x00' + '\x01' + string.ljust('', 256 - 19 - 32 - 3, '\xff') \
+        + '\x00' + digestinfo
+    return int(hexlify(byte_repr), 16)
+
+def compute_signature(key, digestinfo):
+    e = key[4]
+    p = key[5]
+    q = key[6]
+    n = key[7]
+    p1 = p - 1
+    q1 = q - 1
+    h = p1 * q1
+    d = modinv(e, h)
+    dp = d % p1
+    dq = d % q1
+    qp = modinv(q, p)
+
+    input = pkcs1_pad_for_sign(digestinfo)
+    t1 = pow(input, dp, p)
+    t2 = pow(input, dq, q)
+    t = ((t1 - t2) * qp) % p
+    sig = t2 + t * q
+    return sig
+
+def integer_to_bytes_256(i):
+    s = hex(i)[2:]
+    s = s.rstrip('L')
+    if len(s) & 1:
+        s = '0' + s
+    return string.rjust(unhexlify(s), 256, '\x00')
+
+def get_raw_pubkey(key):
+    return key[0]
diff --git a/tool/rsa_example.key b/tool/rsa_example.key
new file mode 100644 (file)
index 0000000..cdf2d5e
--- /dev/null
@@ -0,0 +1,4 @@
+9cf7192b51a574d1ad3ccb08ba09b87f228573893eee355529ff243e90fd4b86f79a82097cc7922c0485bed1616b1656a9b0b19ef78ea8ec34c384019adc5d5bf4db2d2a0a2d9cf14277bdcb7056f48b81214e3f7f7742231e29673966f9b1106862112cc798dba8d4a138bb5abfc6d4c12d53a5d39b2f783da916da20852ee139bbafda61d429caf2a4f30847ce7e7ae32ab4061e27dd9e4d00d60910249db8d8559dd85f7ca59659ef400c8f6318700f4e97f0c6f4165de80641490433c88da8682befe68eb311f54af2b07d97ac74edb5399cf054764211694fbb8d1d333f3269f235abe025067f811ff83a2224826219b309ea3e6c968f42b3e52f245dc9
+010001
+b5ab7b159220b18e363258f61ebde08bae83d6ce2dbfe4adc143628c527887acde9de09bf9b49f438019004d71855f30c2d69b6c29bb9882ab641b3387409fe9199464a7faa4b5230c56d9e17cd9ed074bc00180ebed62bae3af28e6ff2ac2654ad968834c5d5c88f8d9d3cc5e167b10453b049d4e454a5761fb0ac717185907
+dd2fffa9814296156a6926cd17b65564187e424dcadce9b032246ad7e46448bb0f9e0ff3c64f987424b1a40bc694e2e9ac4fb1930d163582d7acf20653a1c44b97846c1c5fd8a7b19bb225fb39c30e25410483deaf8c2538d222b748c4d8103b11cec04f666a5c0dbcbf5d5f625f158f65746c3fafe6418145f7cffa5fadeeaf
diff --git a/tool/upgrade_by_passwd.py b/tool/upgrade_by_passwd.py
new file mode 100755 (executable)
index 0000000..92c9e8d
--- /dev/null
@@ -0,0 +1,111 @@
+#! /usr/bin/python
+
+"""
+upgrade_by_passwd.py - a tool to install another firmware for Gnuk Token
+                       which is just shipped from factory
+
+Copyright (C) 2012 Free Software Initiative of Japan
+Author: NIIBE Yutaka <gniibe@fsij.org>
+
+This file is a part of Gnuk, a GnuPG USB Token implementation.
+
+Gnuk is free software: you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Gnuk is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
+License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+"""
+
+from gnuk_token import *
+import sys, binascii, time, os
+import rsa
+
+DEFAULT_PW3 = "12345678"
+BY_ADMIN = 3
+
+KEYNO_FOR_AUTH=2 
+
+def main(passwd, data_regnual, data_upgrade):
+    l = len(data_regnual)
+    if (l & 0x03) != 0:
+        data_regnual = data_regnual.ljust(l + 4 - (l & 0x03), chr(0))
+    crc32code = crc32(data_regnual)
+    print "CRC32: %04x\n" % crc32code
+    data_regnual += pack('<I', crc32code)
+
+    rsa_key = rsa.read_key_from_file('rsa_example.key')
+    rsa_raw_pubkey = rsa.get_raw_pubkey(rsa_key)
+
+    gnuk = get_gnuk_device()
+    gnuk.cmd_verify(BY_ADMIN, passwd)
+    gnuk.cmd_write_binary(1, rsa_raw_pubkey, False)
+
+    gnuk.cmd_select_openpgp()
+    challenge = gnuk.cmd_get_challenge()
+    digestinfo = binascii.unhexlify(SHA256_OID_PREFIX) + challenge
+    signed = rsa.compute_signature(rsa_key, digestinfo)
+    signed_bytes = rsa.integer_to_bytes_256(signed)
+    gnuk.cmd_external_authenticate(signed_bytes)
+    gnuk.stop_gnuk()
+    mem_info = gnuk.mem_info()
+    print "%08x:%08x" % mem_info
+
+    print "Downloading flash upgrade program..."
+    gnuk.download(mem_info[0], data_regnual)
+    print "Run flash upgrade program..."
+    gnuk.execute(mem_info[0] + len(data_regnual) - 4)
+    #
+    time.sleep(3)
+    gnuk.reset_device()
+    del gnuk
+    gnuk = None
+    #
+    print "Wait 3 seconds..."
+    time.sleep(3)
+    # Then, send upgrade program...
+    reg = None
+    for dev in gnuk_devices_by_vidpid():
+        try:
+            reg = regnual(dev)
+            print "Device: ", dev.filename
+            break
+        except:
+            pass
+    mem_info = reg.mem_info()
+    print "%08x:%08x" % mem_info
+    print "Downloading the program"
+    reg.download(mem_info[0], data_upgrade)
+    reg.protect()
+    reg.finish()
+    reg.reset_device()
+    return 0
+
+if __name__ == '__main__':
+    if os.getcwd() != os.path.dirname(os.path.abspath(__file__)):
+        print "Please change working directory to: %s" % os.path.dirname(os.path.abspath(__file__))
+        exit(1)
+
+    passwd = DEFAULT_PW3
+    if len(sys.argv) > 1 and sys.argv[1] == '-p':
+        from getpass import getpass
+        passwd = getpass("Admin password: ")
+        sys.argv.pop(1)
+    filename_regnual = sys.argv[1]
+    filename_upgrade = sys.argv[2]
+    f = open(filename_regnual)
+    data_regnual = f.read()
+    f.close()
+    print "%s: %d" % (filename_regnual, len(data_regnual))
+    f = open(filename_upgrade)
+    data_upgrade = f.read()
+    f.close()
+    print "%s: %d" % (filename_upgrade, len(data_upgrade))
+    # First 4096-byte in data_upgrade is SYS, so, skip it.
+    main(passwd, data_regnual, data_upgrade[4096:])