Support VERIFY reset feature
[gnuk/gnuk.git] / src / pin-dnd.c
1 /*
2  * pin-dnd.c -- PIN input support (Drag and Drop with File Manager)
3  *
4  * Copyright (C) 2011, 2013 Free Software Initiative of Japan
5  * Author: NIIBE Yutaka <gniibe@fsij.org>
6  *
7  * This file is a part of Gnuk, a GnuPG USB Token implementation.
8  *
9  * Gnuk is free software: you can redistribute it and/or modify it
10  * under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * Gnuk is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
17  * License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21  *
22  */
23
24 #include <stdint.h>
25 #include <string.h>
26 #include <chopstx.h>
27
28 #include "config.h"
29 #include "board.h"
30 #include "gnuk.h"
31 #include "usb-msc.h"
32
33 struct folder {
34   uint8_t parent;
35   uint8_t children[7];
36 };
37
38 static struct folder folders[8];
39 static const struct folder folder_ini = { 0, { 1, 2, 3, 4, 5, 6, 7 } };
40
41
42 uint8_t pin_input_buffer[MAX_PIN_CHARS];
43 uint8_t pin_input_len;
44
45 static uint8_t msg;
46
47 /*
48  * Let user input PIN string.
49  * Return length of the string.
50  * The string itself is in PIN_INPUT_BUFFER.
51  */
52 int
53 pinpad_getline (int msg_code, uint32_t timeout)
54 {
55   uint8_t msg_received;
56
57   (void)msg_code;
58   (void)timeout;
59
60   DEBUG_INFO (">>>\r\n");
61
62   pin_input_len = 0;
63
64   msc_media_insert_change (1);
65
66   memset (folders, 0, sizeof folders);
67   memcpy (folders, &folder_ini, sizeof folder_ini);
68
69   while (1)
70     {
71       chopstx_mutex_lock (pinpad_mutex);
72       chopstx_cond_wait (pinpad_cond, pinpad_mutex);
73       msg_received = msg;
74       chopstx_mutex_unlock (pinpad_mutex);
75
76       led_blink (LED_ONESHOT);
77       if (msg_received != 0)
78         break;
79     }
80
81   msc_media_insert_change (0);
82
83   if (msg == 1)
84     return pin_input_len;
85   else
86     return -1;                  /* cancel */
87 }
88
89 static void pinpad_input (void)
90 {
91   chopstx_mutex_lock (pinpad_mutex);
92   msg = 0;
93   chopstx_cond_signal (pinpad_cond);
94   chopstx_mutex_unlock (pinpad_mutex);
95 }
96
97 static void pinpad_finish_entry (int cancel)
98 {
99   chopstx_mutex_lock (pinpad_mutex);
100   if (cancel)
101     msg = 2;
102   else
103     msg = 1;
104   chopstx_cond_signal (pinpad_cond);
105   chopstx_mutex_unlock (pinpad_mutex);
106 }
107
108 #define TOTAL_SECTOR 68
109
110 /*
111
112 blk=0: master boot record sector
113 blk=1: fat0
114 blk=2: fat1
115 blk=3: root directory
116 blk=4: fat cluster #2
117 ...
118 blk=4+63: fat cluster #2+63
119 */
120
121 static const uint8_t d0_0_sector[] = {
122   0xeb, 0x3c,                          /* Jump instruction */
123   0x90, /* NOP */
124
125   0x6d, 0x6b, 0x64, 0x6f, 0x73, 0x66, 0x73, 0x20, /* "mkdosfs " */
126
127   0x00, 0x02,                   /* Bytes per sector: 512 */
128
129   0x01,                         /* sectors per cluster: 1 */
130   0x01, 0x00,                   /* reserved sector count: 1 */
131   0x02,                         /* Number of FATs: 2 */
132   0x10, 0x00,                   /* Max. root directory entries: 16 (1 sector) */
133   TOTAL_SECTOR, 0x00,           /* total sectors: 68 */
134   0xf8,                         /* media descriptor: fixed disk */
135   0x01, 0x00,                   /* sectors per FAT: 1 */
136   0x04, 0x00,                   /* sectors per track: 4 */
137   0x01, 0x00,                   /* number of heads: 1 */
138   0x00, 0x00, 0x00, 0x00,       /* hidden sectors: 0 */
139   0x00, 0x00, 0x00, 0x00,       /* total sectors (long) */
140   0x00,                         /* drive number */
141   0x00,                         /* reserved */
142   0x29,                         /* extended boot signature */
143   0xbf, 0x86, 0x75, 0xea, /* Volume ID (serial number) (Little endian) */
144
145   /* Volume label: DNDpinentry */
146   'D', 'n', 'D', 'p', 'i', 'n', 'e', 'n', 't', 'r', 'y',
147
148   0x46, 0x41, 0x54, 0x31, 0x32, 0x20, 0x20, 0x20, /* FAT12 */
149
150   0x0e,                         /*    push cs */
151   0x1f,                         /*    pop ds */
152   0xbe, 0x5b, 0x7c,             /*    mov si, offset message_txt */
153   0xac,                         /* 1: lodsb */
154   0x22, 0xc0,                   /*    and al, al */
155   0x74, 0x0b,                   /*    jz 2f */
156   0x56,                         /*    push si */
157   0xb4, 0x0e,                   /*    mov ah, 0eh */
158   0xbb, 0x07, 0x00,             /*    mov bx, 0007h */
159   0xcd, 0x10,                   /*    int 10h ; output char color=white */
160   0x5e,                         /*    pop si */
161   0xeb, 0xf0,                   /*    jmp 1b */
162   0x32, 0xe4,                   /* 2: xor ah, ah */
163   0xcd, 0x16,                   /*    int 16h; key input */
164   0xcd, 0x19,                   /*    int 19h; load OS */
165   0xeb, 0xfe,                   /* 3: jmp 3b */
166
167   /* "This is not a bootable disk... \r\n" */
168   0x54, 0x68, 0x69, 0x73, 0x20,
169   0x69, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x61,
170   0x20, 0x62, 0x6f, 0x6f, 0x74, 0x61, 0x62, 0x6c,
171   0x65, 0x20, 0x64, 0x69, 0x73, 0x6b, 0x2e, 0x20,
172   0x20, 0x50, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x20,
173   0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x20, 0x61,
174   0x20, 0x62, 0x6f, 0x6f, 0x74, 0x61, 0x62, 0x6c,
175   0x65, 0x20, 0x66, 0x6c, 0x6f, 0x70, 0x70, 0x79,
176   0x20, 0x61, 0x6e, 0x64, 0x0d, 0x0a, 0x70, 0x72,
177   0x65, 0x73, 0x73, 0x20, 0x61, 0x6e, 0x79, 0x20,
178   0x6b, 0x65, 0x79, 0x20, 0x74, 0x6f, 0x20, 0x74,
179   0x72, 0x79, 0x20, 0x61, 0x67, 0x61, 0x69, 0x6e,
180   0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x0d, 0x0a, 0x00,
181 };
182
183 static const uint8_t d0_fat0_sector[] = {
184   0xf8, 0xff, 0xff,     /* Media descriptor: fixed disk */ /* EOC */
185   0xff, 0xff, 0xff,     /* cluster 2: used */ /* cluster 3: used */
186   0xff, 0xff, 0xff,     /* cluster 4: used */ /* cluster 5: used */
187   0xff, 0xff, 0xff,     /* cluster 6: used */ /* cluster 7: used */
188   0xff, 0x0f, 0x00,     /* cluster 8: used */ /* cluster 9: free */
189 };
190
191 static uint8_t the_sector[512];
192
193 #define FOLDER_INDEX_TO_CLUSTER_NO(i) (i+1)
194 #define CLUSTER_NO_TO_FOLDER_INDEX(n) (n-1)
195 #define FOLDER_INDEX_TO_LBA(i) (i+3)
196 #define LBA_TO_FOLDER_INDEX(lba) (lba-3)
197 #define FOLDER_INDEX_TO_DIRCHAR(i) ('A'+i-1)
198 #define DIRCHAR_TO_FOLDER_INDEX(c) (c - 'A' + 1)
199
200 static uint8_t *fill_file_entry (uint8_t *p, const uint8_t *filename,
201                                  uint16_t cluster_no)
202 {
203   memcpy (p, filename, 8+3);
204   p += 11;
205   *p++ = 0x10;                  /* directory */
206   *p++ = 0x00;                  /* reserved */
207   memcpy (p, "\x64\x3b\xa7\x61\x3f", 5); /* create time */
208   p += 5;
209   memcpy (p, "\x61\x3f", 2);    /* last access */
210   p += 2;
211   *p++ = 0x00;  *p++ = 0x00;    /* ea-index */
212   memcpy (p, "\x3b\xa7\x61\x3f", 4); /* last modified */
213   p += 4;
214   memcpy (p, &cluster_no, 2);   /* cluster # */
215   p += 2;
216   *p++ = 0x00;  *p++ = 0x00;  *p++ = 0x00;  *p++ = 0x00; /* file size */
217   return p;
218 }
219
220 static void build_directory_sector (uint8_t *p, uint8_t index)
221 {
222   uint16_t cluster_no = FOLDER_INDEX_TO_CLUSTER_NO (index);
223   int i;
224   uint8_t filename[11] = { 0x2e, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
225                            0x20, 0x20, 0x20 };
226   uint8_t child;
227
228   memset (p, 0, 512);
229
230   if (index != 0)
231     {
232       p = fill_file_entry (p, filename, cluster_no);
233       filename[1] = 0x2e;
234       p = fill_file_entry (p, filename, 0);
235       filename[1] = 0x20;
236     }
237
238   for (i = 0; i < 7; i++)
239     if ((child = folders[index].children[i]) != 0)
240       {
241         filename[0] = FOLDER_INDEX_TO_DIRCHAR (child);
242         p = fill_file_entry (p, filename, FOLDER_INDEX_TO_CLUSTER_NO (child));
243       }
244     else
245       break;
246 }
247
248 int
249 msc_scsi_read (uint32_t lba, const uint8_t **sector_p)
250 {
251   if (!media_available)
252     return SCSI_ERROR_NOT_READY;
253
254   if (lba >= TOTAL_SECTOR)
255     return SCSI_ERROR_ILLEAGAL_REQUEST;
256
257   switch (lba)
258     {
259     case 0:
260       *sector_p = the_sector;
261       memcpy (the_sector, d0_0_sector, sizeof d0_0_sector);
262       memset (the_sector + sizeof d0_0_sector, 0, 512 - sizeof d0_0_sector);
263       the_sector[510] = 0x55;
264       the_sector[511] = 0xaa;
265       return 0;
266     case 1:
267     case 2:
268       *sector_p = the_sector;
269       memcpy (the_sector, d0_fat0_sector, sizeof d0_fat0_sector);
270       memset (the_sector + sizeof d0_fat0_sector, 0,
271               512 - sizeof d0_fat0_sector);
272       return 0;
273     case 3:
274     case 4:
275     case 5:
276     case 6:
277     case 7:
278     case 8:
279     case 9:
280     case 10:
281       *sector_p = the_sector;
282       build_directory_sector (the_sector, LBA_TO_FOLDER_INDEX (lba));
283       return 0;
284     default:
285       *sector_p = the_sector;
286       memset (the_sector, 0, 512);
287       return 0;
288     }
289 }
290
291
292 static void parse_directory_sector (const uint8_t *p, uint8_t index)
293 {
294   int i;
295   uint8_t child;
296   int input = 0;
297   int num_children = 0;
298
299   if (index != 0)
300     {
301       uint16_t cluster_no;
302       uint8_t dest_index;
303
304       p += 32;          /* skip "." */
305
306       /* ".." */
307       cluster_no = p[26] | (p[27] << 8);
308       dest_index = CLUSTER_NO_TO_FOLDER_INDEX (cluster_no);
309
310       if (dest_index < 1 || dest_index > 7)
311         ; /* it can be 255 : root_dir */
312       else
313         if (pin_input_len < MAX_PIN_CHARS - 2)
314           {
315             pin_input_buffer[pin_input_len++]
316               = FOLDER_INDEX_TO_DIRCHAR (index);
317             pin_input_buffer[pin_input_len++]
318               = FOLDER_INDEX_TO_DIRCHAR (dest_index);
319             input = 1;
320           }
321
322       p += 32;
323     }
324
325   for (i = 0; i < 7; i++)
326     {
327       if (*p >= 'A' && *p <= 'G')
328         {
329           child = DIRCHAR_TO_FOLDER_INDEX (*p);
330           folders[index].children[i] = child;
331           num_children++;
332         }
333       else
334         folders[index].children[i] = 0;
335       p += 32;
336     }
337
338   if (index == 0 && num_children == 1)
339     pinpad_finish_entry (0);
340   else if (input)
341     pinpad_input ();
342 }
343
344 int
345 msc_scsi_write (uint32_t lba, const uint8_t *buf, size_t size)
346 {
347   (void)size;
348
349   if (!media_available)
350     return SCSI_ERROR_NOT_READY;
351
352   if (lba >= TOTAL_SECTOR)
353     return SCSI_ERROR_ILLEAGAL_REQUEST;
354
355   if (lba == 1)
356     return 0;                   /* updating FAT, just ignore */
357
358   if (lba <= 2 || lba >= 11)
359     return SCSI_ERROR_DATA_PROTECT;
360   else
361     {
362       uint8_t index = LBA_TO_FOLDER_INDEX (lba);
363
364       parse_directory_sector (buf, index);
365       return 0;
366     }
367 }
368
369 void
370 msc_scsi_stop (uint8_t code)
371 {
372   (void)code;
373   pinpad_finish_entry (1);
374 }