4bd09c9a167d251170a986fa2ac184ff7885fc43
[gnuk/gnuk.git] / tool / pageant_proxy_to_gpg.py
1 import os, sys, re, hashlib, binascii
2 from struct import *
3 from gpg_agent imort gpg_agent
4
5 # Assume it's only OPENPGP.3 key
6
7 def debug(str):
8     print "DEBUG: ", str
9     sys.stdout.flush()
10
11 def get_keylist(keyinfo_result):
12     kl_str = keyinfo_result[0:-1] # Chop last newline
13     kl = kl_str.split('\n')
14     # filter by "OPENPGP.3", and only keygrip
15     return [kg.split(' ')[0] for kg in kl if re.search("OPENPGP\\.3", kg)]
16
17 g = gpg_agent()
18 g.read_line()                   # Greeting message
19
20 g.send_command('KEYINFO --list --data\n')
21 keyinfo_result = g.get_response()
22 kl = get_keylist(keyinfo_result)
23 print kl
24
25 keygrip = kl[0]
26
27 g.send_command('READKEY %s\n' % keygrip)
28 key = g.get_response
29 pos = key.index("(10:public-key(3:rsa(1:n257:") + 28
30 pos_last = key.index(")(1:e3:")
31 n = key[pos:pos_last]
32 e = key[pos_last+7:pos_last+10]
33 if len(n) != 257:
34     raise ValueError(keygrip)
35 print binascii.hexlify(n)
36 print binascii.hexlify(e)
37
38 ssh_rsa_public_blob = "\x00\x00\x00\x07ssh-rsa" + \
39     "\x00\x00\x00\x03" + e + "\x00\x00\x01\x01" + n
40
41 ssh_key_comment = "key_on_gpg"  # XXX: get login from card?
42
43 import win32con, win32api, win32gui, ctypes, ctypes.wintypes
44
45 class COPYDATA(ctypes.Structure):
46     _fields_ = [ ('dwData', ctypes.wintypes.LPARAM),
47                  ('cbData', ctypes.wintypes.DWORD),
48                  ('lpData', ctypes.c_void_p) ]
49
50 P_COPYDATA = ctypes.POINTER(COPYDATA)
51
52 class SSH_MSG_HEAD(ctypes.BigEndianStructure):
53     _pack_ = 1
54     _fields_ = [ ('msg_len', ctypes.c_uint32),
55                  ('msg_type', ctypes.c_byte) ]
56
57 P_SSH_MSG_HEAD = ctypes.POINTER(SSH_MSG_HEAD)
58
59 class SSH_MSG_ID_ANSWER_HEAD(ctypes.BigEndianStructure):
60     _pack_ = 1
61     _fields_ = [ ('msg_len', ctypes.c_uint32),
62                  ('msg_type', ctypes.c_byte),
63                  ('keys', ctypes.c_uint32)]
64
65 P_SSH_MSG_ID_ANSWER = ctypes.POINTER(SSH_MSG_ID_ANSWER_HEAD)
66
67 class SSH_MSG_SIGN_RESPONSE_HEAD(ctypes.BigEndianStructure):
68     _pack_ = 1
69     _fields_ = [ ('msg_len', ctypes.c_uint32),
70                  ('msg_type', ctypes.c_byte),
71                  ('sig_len', ctypes.c_uint32)]
72
73 P_SSH_MSG_SIGN_RESPONSE = ctypes.POINTER(SSH_MSG_SIGN_RESPONSE_HEAD)
74
75
76 FILE_MAP_ALL_ACCESS=0x000F001F
77
78 class windows_ipc_listener(object):
79     def __init__(self):
80         message_map = { win32con.WM_COPYDATA: self.OnCopyData }
81         wc = win32gui.WNDCLASS()
82         wc.lpfnWndProc = message_map
83         wc.lpszClassName = 'Pageant'
84         hinst = wc.hInstance = win32api.GetModuleHandle(None)
85         classAtom = win32gui.RegisterClass(wc)
86         self.hwnd = win32gui.CreateWindow (
87             classAtom,
88             "Pageant",
89             0,
90             0, 
91             0,
92             win32con.CW_USEDEFAULT, 
93             win32con.CW_USEDEFAULT,
94             0, 
95             0,
96             hinst, 
97             None
98         )
99         debug("created: window=%08x" % self.hwnd)
100
101     def OnCopyData(self, hwnd, msg, wparam, lparam):
102         debug("WM_COPYDATA message")
103         debug("  window=%08x" % hwnd)
104         debug("  msg   =%08x" % msg)
105         debug("  wparam=%08x" % wparam)
106         pCDS = ctypes.cast(lparam, P_COPYDATA)
107         debug("  dwData=%08x" % (pCDS.contents.dwData & 0xffffffff))
108         debug("  len=%d" % pCDS.contents.cbData)
109         mapname = ctypes.string_at(pCDS.contents.lpData)
110         debug("  mapname='%s'" % ctypes.string_at(pCDS.contents.lpData))
111         hMapObject = ctypes.windll.kernel32.OpenFileMappingA(FILE_MAP_ALL_ACCESS, 0, mapname)
112         if hMapObject == 0:
113             debug("error on OpenFileMapping")
114             return 0
115         pBuf = ctypes.windll.kernel32.MapViewOfFile(hMapObject, FILE_MAP_ALL_ACCESS, 0, 0, 0)
116         if pBuf == 0:
117             ctypes.windll.kernel32.CloseHandle(hMapObject)
118             debug("error on MapViewOfFile")
119             return 0
120         pSshMsg = ctypes.cast(pBuf, P_SSH_MSG_HEAD)
121         debug("   ssh_msg_len: %d" % pSshMsg.contents.msg_len)
122         debug("   ssh_msg_type: %d" % pSshMsg.contents.msg_type)
123         if pSshMsg.contents.msg_type == 11: # SSH2_AGENT_REQUEST_IDENTITIES
124             blob_len = len(ssh_rsa_public_blob)
125             cmnt_len = len(ssh_key_comment)
126             pAns = ctypes.cast(pBuf, P_SSH_MSG_ID_ANSWER)
127             pAns.contents.msg_len = 1+4+4+blob_len+4+cmnt_len
128             pAns.contents.msg_type = 12 # SSH2_AGENT_IDENTITIES_ANSWER
129             pAns.contents.keys = 1
130             ctypes.memmove(pBuf+4+1+4, pack('>I', blob_len), 4)
131             ctypes.memmove(pBuf+4+1+4+4, ssh_rsa_public_blob, blob_len)
132             ctypes.memmove(pBuf+4+1+4+4+blob_len, pack('>I', cmnt_len), 4)
133             ctypes.memmove(pBuf+4+1+4+4+blob_len+4, ssh_key_comment, cmnt_len)
134
135             debug("answer is:")
136             debug("   ssh_msg_len: %d" % pSshMsg.contents.msg_len)
137             debug("   ssh_msg_type: %d" % pSshMsg.contents.msg_type)
138         elif pSshMsg.contents.msg_type == 13: # SSH2_AGENT_SIGN_REQUEST
139             req_blob_len = unpack(">I", ctypes.string_at(pBuf+5, 4))[0]
140             req_blob = ctypes.string_at(pBuf+5+4, req_blob_len)
141             req_data_len = unpack(">I", ctypes.string_at(pBuf+5+4+req_blob_len,4))[0]
142             req_data = ctypes.string_at(pBuf+5+4+req_blob_len+4,req_data_len)
143             debug("    blob_len=%d" % req_blob_len)
144             debug("    data_len=%d" % req_data_len)
145             hash = hashlib.sha1(req_data).hexdigest()
146             debug("    hash=%s" % hash)
147             g.send_command('SIGKEY %s\n' % keygrip)
148             g.send_command('SETHASH --hash=sha1 %s\n' % hash)
149             g.send_command('PKSIGN\n')
150             signature = g.get_response()
151             pos = signature.index("(7:sig-val(3:rsa(1:s256:") + 24
152             signature = "\x00\x00\x00\x07" + "ssh-rsa" + "\x00\x00\x01\x00" + signature[pos:pos+256]
153             siglen = len(signature)
154             debug("sig_len=%d" % siglen)
155             debug("sig=%s" % binascii.hexlify(signature))
156             pRes = ctypes.cast(pBuf, P_SSH_MSG_SIGN_RESPONSE)
157             pRes.contents.msg_len = 1+4+siglen
158             pRes.contents.msg_type = 14 # SSH2_AGENT_SIGN_RESPONSE
159             pRes.contents.sig_len = siglen
160             ctypes.memmove(pBuf+4+1+4, signature, siglen)
161             debug("answer is:")
162             debug("   ssh_msg_len: %d" % pSshMsg.contents.msg_len)
163             debug("   ssh_msg_type: %d" % pSshMsg.contents.msg_type)
164         else:
165             exit(0)
166         ctypes.windll.kernel32.UnmapViewOfFile(pBuf)
167         ctypes.windll.kernel32.CloseHandle(hMapObject)
168         debug("   ssh_msg: done")
169         return 1
170
171 l = windows_ipc_listener()
172 win32gui.PumpMessages()