Line data Source code
1 : /**
2 : * The MIT License (MIT)
3 : *
4 : * Copyright (c) 2021 RSK Labs Ltd
5 : *
6 : * Permission is hereby granted, free of charge, to any person obtaining a copy
7 : * of this software and associated documentation files (the "Software"), to
8 : * deal in the Software without restriction, including without limitation the
9 : * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 : * sell copies of the Software, and to permit persons to whom the Software is
11 : * furnished to do so, subject to the following conditions:
12 : *
13 : * The above copyright notice and this permission notice shall be included in
14 : * all copies or substantial portions of the Software.
15 : *
16 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 : * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 : * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 : * IN THE SOFTWARE.
23 : */
24 :
25 : #include <string.h>
26 :
27 : #include "apdu.h"
28 : #include "os.h"
29 : #include "ui_err.h"
30 : #include "pin.h"
31 :
32 : // Helper macros for pin validation
33 : #define IS_IN_RANGE(c, begin, end) (((c) >= (begin)) && ((c) <= (end)))
34 : #define IS_ALPHA(c) (IS_IN_RANGE(c, 'a', 'z') || IS_IN_RANGE(c, 'A', 'Z'))
35 : #define IS_NUM(c) IS_IN_RANGE(c, '0', '9')
36 : #define IS_ALPHANUM(c) (IS_ALPHA(c) || IS_NUM(c))
37 :
38 : #define PIN_LENGTH 8
39 : #define PIN_BUFFER_LENGTH (PIN_LENGTH + 2)
40 : // Internal PIN buffer used for authenticated operations
41 : unsigned char G_pin_buffer[PIN_BUFFER_LENGTH];
42 : // Helper macros for pin manipulation when prepended length is used
43 : #define GET_PIN() ((unsigned char *)(G_pin_buffer + 1))
44 : #define GET_PIN_LENGTH() strlen((const char *)GET_PIN())
45 :
46 : /*
47 : * Implements RSK PIN command.
48 : *
49 : * Receives one byte at a time and updates the pin context, adding a null byte
50 : * at the end.
51 : *
52 : * @arg[in] rx number of received bytes from the Host
53 : * @ret number of transmited bytes to the host
54 : */
55 240 : unsigned int update_pin_buffer(volatile unsigned int rx) {
56 : // Should receive 1 byte per call
57 240 : if (APDU_DATA_SIZE(rx) != 1) {
58 0 : THROW(ERR_UI_PROT_INVALID);
59 : }
60 :
61 240 : unsigned char index = APDU_OP();
62 240 : if ((index >= 0) && (index <= PIN_LENGTH)) {
63 223 : G_pin_buffer[index] = APDU_AT(DATA);
64 223 : G_pin_buffer[index + 1] = 0;
65 : }
66 :
67 240 : return 3;
68 : }
69 :
70 : /*
71 : * Implements RSK NEW PIN command.
72 : *
73 : * Sets and checks the device pin.
74 : *
75 : * @ret number of transmited bytes to the host
76 : */
77 5 : unsigned int set_pin() {
78 : #ifndef DEBUG_BUILD
79 5 : if (!is_pin_valid()) {
80 1 : THROW(ERR_UI_INVALID_PIN);
81 : }
82 : #endif
83 : // Set PIN
84 4 : os_perso_set_pin(0, GET_PIN(), GET_PIN_LENGTH());
85 : // check PIN
86 4 : os_global_pin_invalidate();
87 4 : unsigned char output_index = CMDPOS;
88 4 : SET_APDU_AT(output_index++, 2);
89 4 : SET_APDU_AT(output_index++,
90 : os_global_pin_check(GET_PIN(), GET_PIN_LENGTH()));
91 4 : return output_index;
92 : }
93 :
94 : /*
95 : * Validates that the pin curently saved to the internal buffer has exactly
96 : * PIN_LENGTH alphanumeric characters with at least one alphabetic character.
97 : *
98 : * @ret true if pin is valid, false otherwise
99 : */
100 25 : bool is_pin_valid() {
101 : // PIN_LENGTH is the only length accepted
102 25 : if (GET_PIN_LENGTH() != PIN_LENGTH) {
103 9 : return false;
104 : }
105 : // Check if PIN is alphanumeric
106 16 : bool hasAlpha = false;
107 136 : for (int i = 0; i < PIN_LENGTH; i++) {
108 121 : if (!IS_ALPHANUM(GET_PIN()[i])) {
109 1 : return false;
110 : }
111 120 : if (hasAlpha || IS_ALPHA(GET_PIN()[i])) {
112 66 : hasAlpha = true;
113 : }
114 : }
115 :
116 15 : return hasAlpha;
117 : }
118 :
119 : /*
120 : * Fills the internal pin buffer with zeroes
121 : */
122 0 : void clear_pin() {
123 0 : explicit_bzero(G_pin_buffer, sizeof(G_pin_buffer));
124 0 : }
125 :
126 : /*
127 : * Uses the pin currently saved to the internal pin buffer to unlock the device
128 : *
129 : * @arg[in] prepended_length true if the internal buffer includes a prepended
130 : * length byte, false otherwise
131 : * @ret 1 if pin validated successfully, 0 otherwise
132 : */
133 4 : unsigned int unlock_with_pin(bool prepended_length) {
134 4 : if (prepended_length) {
135 4 : return os_global_pin_check(GET_PIN(), GET_PIN_LENGTH());
136 : } else {
137 0 : return os_global_pin_check(G_pin_buffer,
138 0 : strlen((const char *)G_pin_buffer));
139 : }
140 : }
141 :
142 : /*
143 : * Sets the pin currently saved to the internal pin buffer as the device's pin.
144 : * This function assumes the pin is saved with a prepended length byte.
145 : */
146 1 : void set_device_pin() {
147 1 : os_perso_set_pin(0, GET_PIN(), GET_PIN_LENGTH());
148 1 : }
|