yubikey 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ (The MIT License)
2
+
3
+ Copyright (c) 2009 Jonathan Rudenberg
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ 'Software'), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,33 @@
1
+ = yubikey
2
+
3
+ == Description
4
+
5
+ A library to decode, decrypt and parse Yubikey[http://www.yubico.com/home/index/] one-time passwords.
6
+
7
+ == Usage
8
+
9
+ key = 'ecde18dbe76fbd0c33330f1c354871db'
10
+ otp = 'dteffujehknhfjbrjnlnldnhcujvddbikngjrtgh'
11
+ token = Yubikey::OTP.new(otp, key)
12
+
13
+ p "Device public id: #{token.public_id}" #=> 'dteffuje'
14
+ p "Device secret id: #{token.secret_id}" #=> '8792ebfe26cc'
15
+ p "Device insertions: #{token.insert_counter}" #=> 19
16
+ p "Session activation counter: #{token.session_counter}" #=> 17
17
+ p "Session timestamp: #{token.timestamp}" #=> 49712
18
+ p "OTP random data: #{token.random_number}" #=> 40904
19
+
20
+ == Install
21
+
22
+ sudo gem install yubikey
23
+
24
+ == Copyright
25
+
26
+ === Ruby library
27
+ Written by Jonathan Rudenberg <jon335@gmail.com>
28
+ Copyright (c) 2009 Jonathan Rudenberg
29
+ The MIT License. See LICENSE.
30
+
31
+ === AES, ModHex, and CRC code
32
+ Written by Simon Josefsson <simon@josefsson.org>
33
+ Copyright (c) 2006, 2007, 2008, 2009 Yubico AB
data/examples/otp.rb ADDED
@@ -0,0 +1,12 @@
1
+ require 'yubikey'
2
+
3
+ key = 'ecde18dbe76fbd0c33330f1c354871db'
4
+ otp = 'dteffujehknhfjbrjnlnldnhcujvddbikngjrtgh'
5
+ token = Yubikey::OTP.new(otp, key)
6
+
7
+ p "Device public id: #{token.public_id}"
8
+ p "Device secret id: #{token.secret_id}"
9
+ p "Device insertions: #{token.insert_counter}"
10
+ p "Session activation counter: #{token.session_counter}"
11
+ p "Session timestamp: #{token.timestamp}"
12
+ p "OTP random data: #{token.random_number}"
@@ -0,0 +1,7 @@
1
+ require 'mkmf'
2
+
3
+ dir_config('yubikey_ext')
4
+
5
+ have_header('yubikey.h')
6
+
7
+ create_makefile('yubikey_ext')
@@ -0,0 +1,214 @@
1
+ /* ykaes.c --- Implementation of AES-128.
2
+ *
3
+ * Copyright (c) 2006, 2007, 2008, 2009 Yubico AB
4
+ * All rights reserved.
5
+ *
6
+ * Redistribution and use in source and binary forms, with or without
7
+ * modification, are permitted provided that the following conditions are
8
+ * met:
9
+ *
10
+ * * Redistributions of source code must retain the above copyright
11
+ * notice, this list of conditions and the following disclaimer.
12
+ *
13
+ * * Redistributions in binary form must reproduce the above
14
+ * copyright notice, this list of conditions and the following
15
+ * disclaimer in the documentation and/or other materials provided
16
+ * with the distribution.
17
+ *
18
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+ *
30
+ */
31
+
32
+ #include "yubikey.h"
33
+
34
+ #define NUMBER_OF_ROUNDS 10
35
+
36
+ static const uint8_t RC[] =
37
+ { 0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36 };
38
+
39
+ static const uint8_t rijndael_sbox[] = {
40
+ 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5,
41
+ 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
42
+ 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0,
43
+ 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
44
+ 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC,
45
+ 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
46
+ 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A,
47
+ 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
48
+ 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0,
49
+ 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
50
+ 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B,
51
+ 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
52
+ 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85,
53
+ 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
54
+ 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5,
55
+ 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
56
+ 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17,
57
+ 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
58
+ 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88,
59
+ 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
60
+ 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C,
61
+ 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
62
+ 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9,
63
+ 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
64
+ 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6,
65
+ 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
66
+ 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E,
67
+ 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
68
+ 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94,
69
+ 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
70
+ 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68,
71
+ 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16
72
+ };
73
+
74
+ static const uint8_t rijndael_inv_sbox[] = {
75
+ 0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38,
76
+ 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB,
77
+ 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87,
78
+ 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB,
79
+ 0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D,
80
+ 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E,
81
+ 0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2,
82
+ 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25,
83
+ 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16,
84
+ 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92,
85
+ 0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA,
86
+ 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84,
87
+ 0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A,
88
+ 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06,
89
+ 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02,
90
+ 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B,
91
+ 0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA,
92
+ 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73,
93
+ 0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85,
94
+ 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E,
95
+ 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89,
96
+ 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B,
97
+ 0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20,
98
+ 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4,
99
+ 0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31,
100
+ 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F,
101
+ 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D,
102
+ 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF,
103
+ 0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0,
104
+ 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61,
105
+ 0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26,
106
+ 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D
107
+ };
108
+
109
+ static inline uint8_t
110
+ xtime (uint8_t b)
111
+ {
112
+ return (b & 0x80) ? ((b << 1) ^ 0x1b) : (b << 1);
113
+ }
114
+
115
+ void
116
+ yubikey_aes_decrypt (uint8_t * state, const uint8_t * key)
117
+ {
118
+ uint8_t i, j, round_key[0x10];
119
+ uint8_t a02x, a13x;
120
+ uint8_t a02xx, a13xx;
121
+ uint8_t k1, k2;
122
+
123
+ memcpy (round_key, key, sizeof (round_key));
124
+ for (i = 0; i < NUMBER_OF_ROUNDS; i++)
125
+ {
126
+ round_key[0] ^= RC[i];
127
+
128
+ round_key[0] ^= rijndael_sbox[round_key[13]];
129
+ round_key[1] ^= rijndael_sbox[round_key[14]];
130
+ round_key[2] ^= rijndael_sbox[round_key[15]];
131
+ round_key[3] ^= rijndael_sbox[round_key[12]];
132
+
133
+ for (j = 4; j < 16; j++)
134
+ round_key[j] ^= round_key[j - 4];
135
+ }
136
+ for (i = 0; i < 0x10; i++)
137
+ state[i] ^= round_key[i];
138
+
139
+ for (i = 1; i <= NUMBER_OF_ROUNDS; i++)
140
+ {
141
+ // inv_byte_sub_shift_row();
142
+
143
+ /* First row: 0 shift, 0 4 8 12 */
144
+ state[0] = rijndael_inv_sbox[state[0]];
145
+ state[4] = rijndael_inv_sbox[state[4]];
146
+ state[8] = rijndael_inv_sbox[state[8]];
147
+ state[12] = rijndael_inv_sbox[state[12]];
148
+
149
+ /* Second row: -1 shift, 1 5 9 13 */
150
+ j = state[13];
151
+ state[13] = rijndael_inv_sbox[state[9]];
152
+ state[9] = rijndael_inv_sbox[state[5]];
153
+ state[5] = rijndael_inv_sbox[state[1]];
154
+ state[1] = rijndael_inv_sbox[j];
155
+
156
+ /* Third row: -2 shift, 2 6 10 14 */
157
+ j = state[2];
158
+ state[2] = rijndael_inv_sbox[state[10]];
159
+ state[10] = rijndael_inv_sbox[j];
160
+ j = state[6];
161
+ state[6] = rijndael_inv_sbox[state[14]];
162
+ state[14] = rijndael_inv_sbox[j];
163
+
164
+ /* Fourth row: -3 shift, 3 7 11 15 */
165
+ j = state[3];
166
+ state[3] = rijndael_inv_sbox[state[7]];
167
+ state[7] = rijndael_inv_sbox[state[11]];
168
+ state[11] = rijndael_inv_sbox[state[15]];
169
+ state[15] = rijndael_inv_sbox[j];
170
+
171
+ // get_inv_round_key(i);
172
+
173
+ for (j = 15; j > 3; j--)
174
+ round_key[j] ^= round_key[j - 4];
175
+
176
+ round_key[0] ^=
177
+ (RC[NUMBER_OF_ROUNDS - i] ^ rijndael_sbox[round_key[13]]);
178
+
179
+ round_key[1] ^= rijndael_sbox[round_key[14]];
180
+ round_key[2] ^= rijndael_sbox[round_key[15]];
181
+ round_key[3] ^= rijndael_sbox[round_key[12]];
182
+
183
+ for (j = 0; j < 16; j++)
184
+ state[j] ^= round_key[j];
185
+ if (i != NUMBER_OF_ROUNDS)
186
+ {
187
+
188
+ //inv_mix_column();
189
+
190
+ for (j = 0; j < 16; j += 4)
191
+ {
192
+ k1 = state[j] ^ state[j + 2];
193
+ a02x = xtime (k1);
194
+ k2 = state[j + 1] ^ state[j + 3];
195
+ a13x = xtime (k2);
196
+
197
+ k1 ^= (k2 ^ xtime (state[j + 1] ^ state[j + 2]));
198
+ k2 = k1;
199
+
200
+ a02xx = xtime (a02x);
201
+ a13xx = xtime (a13x);
202
+
203
+ k1 ^= (xtime (a02xx ^ a13xx) ^ a02xx);
204
+ k2 ^= (xtime (a02xx ^ a13xx) ^ a13xx);
205
+
206
+ state[j] ^= (k1 ^ a02x);
207
+ state[j + 1] ^= k2;
208
+ state[j + 2] ^= (k1 ^ a13x);
209
+ state[j + 3] ^= (k2 ^ a02x ^ a13x);
210
+ }
211
+ }
212
+
213
+ }
214
+ }
@@ -0,0 +1,54 @@
1
+ /* ykcrc.c --- Implementation of Yubikey CRC-16 function.
2
+ *
3
+ * Written by Simon Josefsson <simon@josefsson.org>.
4
+ * Copyright (c) 2006, 2007, 2008, 2009 Yubico AB
5
+ * All rights reserved.
6
+ *
7
+ * Redistribution and use in source and binary forms, with or without
8
+ * modification, are permitted provided that the following conditions are
9
+ * met:
10
+ *
11
+ * * Redistributions of source code must retain the above copyright
12
+ * notice, this list of conditions and the following disclaimer.
13
+ *
14
+ * * Redistributions in binary form must reproduce the above
15
+ * copyright notice, this list of conditions and the following
16
+ * disclaimer in the documentation and/or other materials provided
17
+ * with the distribution.
18
+ *
19
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
+ *
31
+ */
32
+
33
+ #include "yubikey.h"
34
+
35
+ uint16_t
36
+ yubikey_crc16 (const uint8_t *buf, size_t buf_size)
37
+ {
38
+ uint16_t m_crc = 0xffff;
39
+
40
+ while (buf_size--)
41
+ {
42
+ int i, j;
43
+ m_crc ^= (uint8_t) * buf++ & 0xFF;
44
+ for (i = 0; i < 8; i++)
45
+ {
46
+ j = m_crc & 1;
47
+ m_crc >>= 1;
48
+ if (j)
49
+ m_crc ^= 0x8408;
50
+ }
51
+ }
52
+
53
+ return m_crc;
54
+ }
@@ -0,0 +1,76 @@
1
+ /* ykmodhex.c --- Implementation of modhex encoding/decoding
2
+ *
3
+ * Written by Simon Josefsson <simon@josefsson.org>.
4
+ * Copyright (c) 2006, 2007, 2008, 2009 Yubico AB
5
+ * All rights reserved.
6
+ *
7
+ * Redistribution and use in source and binary forms, with or without
8
+ * modification, are permitted provided that the following conditions are
9
+ * met:
10
+ *
11
+ * * Redistributions of source code must retain the above copyright
12
+ * notice, this list of conditions and the following disclaimer.
13
+ *
14
+ * * Redistributions in binary form must reproduce the above
15
+ * copyright notice, this list of conditions and the following
16
+ * disclaimer in the documentation and/or other materials provided
17
+ * with the distribution.
18
+ *
19
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
+ *
31
+ */
32
+
33
+ #include "yubikey.h"
34
+
35
+ #include <stdbool.h>
36
+
37
+ static const char trans[] = YUBIKEY_MODHEX_MAP;
38
+
39
+ void
40
+ yubikey_modhex_encode (char *dst, const char *src, size_t srcSize)
41
+ {
42
+ while (srcSize--)
43
+ {
44
+ *dst++ = trans[(*src >> 4) & 0xf];
45
+ *dst++ = trans[*src++ & 0xf];
46
+ }
47
+
48
+ *dst = '\0';
49
+ }
50
+
51
+ void
52
+ yubikey_modhex_decode (char *dst, const char *src, size_t dstSize)
53
+ {
54
+ char b;
55
+ bool flag = false;
56
+ char *p1;
57
+
58
+ for (; *src && dstSize > 0; src++)
59
+ {
60
+ if ((p1 = strchr (trans, *src)) == NULL)
61
+ b = 0;
62
+ else
63
+ b = (char) (p1 - trans);
64
+
65
+ if ((flag = !flag))
66
+ *dst = b;
67
+ else
68
+ {
69
+ *dst = (*dst << 4) | b;
70
+ dst++;
71
+ dstSize--;
72
+ }
73
+ }
74
+ while (dstSize--)
75
+ *dst++ = 0;
76
+ }
@@ -0,0 +1,75 @@
1
+ /* yubikey.h --- Prototypes for low-level Yubikey OTP functions.
2
+ *
3
+ * Written by Simon Josefsson <simon@josefsson.org>.
4
+ * Copyright (c) 2006, 2007, 2008, 2009 Yubico AB
5
+ * All rights reserved.
6
+ *
7
+ * Redistribution and use in source and binary forms, with or without
8
+ * modification, are permitted provided that the following conditions are
9
+ * met:
10
+ *
11
+ * * Redistributions of source code must retain the above copyright
12
+ * notice, this list of conditions and the following disclaimer.
13
+ *
14
+ * * Redistributions in binary form must reproduce the above
15
+ * copyright notice, this list of conditions and the following
16
+ * disclaimer in the documentation and/or other materials provided
17
+ * with the distribution.
18
+ *
19
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
+ *
31
+ */
32
+
33
+ #ifndef YUBIKEY_H
34
+ #define YUBIKEY_H
35
+
36
+ #include <stdint.h>
37
+ #include <string.h>
38
+
39
+ #define YUBIKEY_BLOCK_SIZE 16
40
+ #define YUBIKEY_KEY_SIZE 16
41
+ #define YUBIKEY_UID_SIZE 6
42
+
43
+ #define YUBIKEY_CRC_OK_RESIDUE 0xf0b8
44
+
45
+ #define yubikey_crc_ok_p(tok) (yubikey_crc16 ((tok), YUBIKEY_BLOCK_SIZE) == YUBIKEY_CRC_OK_RESIDUE)
46
+
47
+ /*
48
+ * Low-level functions; ModHex.
49
+ */
50
+
51
+ #define YUBIKEY_MODHEX_MAP "cbdefghijklnrtuv"
52
+
53
+ /* ModHex encode input string SRC of length SRCSIZE and put the zero
54
+ terminated output string in DST. The size of the output string DST
55
+ must be at least 2*SRCSIZE+1. The output string is always
56
+ 2*SRCSIZE large plus the terminating zero. */
57
+ extern void yubikey_modhex_encode(char *dst, const char *src, size_t srcsize);
58
+
59
+ /* ModHex decode input string SRC of length DSTSIZE/2 into output
60
+ string DST. The output string DST is always DSTSIZE/2 large plus
61
+ the terminating zero. */
62
+ extern void yubikey_modhex_decode(char *dst, const char *src, size_t dstsize);
63
+
64
+ /* Low-level functions; AES. */
65
+
66
+ /* AES-decrypt one 16-byte block STATE using the 128-bit KEY, leaving
67
+ the decrypted output in the STATE buffer. */
68
+ void yubikey_aes_decrypt(uint8_t *state, const uint8_t *key);
69
+
70
+ /*
71
+ * Low-level functions; CRC.
72
+ */
73
+ uint16_t yubikey_crc16 (const uint8_t *buf, size_t buf_size);
74
+
75
+ #endif
@@ -0,0 +1,76 @@
1
+ #include "ruby.h"
2
+ #include "yubikey.h"
3
+
4
+ /*
5
+ * call-seq:
6
+ * decode(modhex_string) -> string
7
+ *
8
+ * Decode a ModHex string into binary data
9
+ */
10
+ static VALUE
11
+ modhex_decode(VALUE self, VALUE modhex_string) {
12
+ char* modhex_string_ptr = StringValuePtr(modhex_string);
13
+ size_t modhex_string_size = strlen(modhex_string_ptr);
14
+ size_t decoded_string_size = modhex_string_size/2;
15
+ char* decoded_string;
16
+
17
+ if (modhex_string_size % 2 != 0)
18
+ rb_raise(rb_eArgError, "ModHex string length is not even");
19
+
20
+ yubikey_modhex_decode(decoded_string, modhex_string_ptr, modhex_string_size);
21
+
22
+ return rb_str_new(decoded_string, decoded_string_size);
23
+ }
24
+
25
+ /*
26
+ * call-seq:
27
+ * decrypt(state, key) -> plaintext
28
+ *
29
+ * Decrypt 16 bytes of binary AES ciphertext to binary plaintext with the Yubico implementation of AES-128 ECB
30
+ *
31
+ * [+state+] 16 bytes of binary ciphertext
32
+ * [+key+] 16-byte binary key
33
+ *
34
+ */
35
+ static VALUE
36
+ aes_decrypt(VALUE self, VALUE state, VALUE key) {
37
+ char* state_ptr = StringValuePtr(state);
38
+ char* key_ptr = StringValuePtr(key);
39
+
40
+ if (RSTRING(state)->len != YUBIKEY_BLOCK_SIZE || RSTRING(key)->len != YUBIKEY_KEY_SIZE)
41
+ rb_raise(rb_eArgError, "key and state must be 16 bytes");
42
+
43
+ yubikey_aes_decrypt((uint8_t*)state_ptr, (uint8_t*)key_ptr);
44
+
45
+ return rb_str_new(state_ptr, YUBIKEY_BLOCK_SIZE);
46
+ }
47
+
48
+ /*
49
+ * call-seq:
50
+ * valid?(token)
51
+ *
52
+ * Check the CRC of a decrypted Yubikey OTP
53
+ *
54
+ * [+token+] 16-byte binary token
55
+ */
56
+ static VALUE
57
+ crc_check(VALUE self, VALUE token) {
58
+ char* token_ptr = StringValuePtr(token);
59
+
60
+ if (RSTRING(token)->len != YUBIKEY_BLOCK_SIZE)
61
+ rb_raise(rb_eArgError, "token must be 16 bytes");
62
+
63
+ return yubikey_crc_ok_p((uint8_t*)token_ptr) ? Qtrue : Qfalse;
64
+ }
65
+
66
+ void
67
+ Init_yubikey_ext() {
68
+ VALUE rb_mYubikey = rb_define_module("Yubikey");
69
+ VALUE rb_mYubikeyAES = rb_define_module_under(rb_mYubikey, "AES");
70
+ VALUE rb_mYubikeyModHex = rb_define_module_under(rb_mYubikey, "ModHex");
71
+ VALUE rb_mYubikeyCRC = rb_define_module_under(rb_mYubikey, "CRC");
72
+
73
+ rb_define_module_function(rb_mYubikeyModHex, "decode", modhex_decode, 1);
74
+ rb_define_module_function(rb_mYubikeyAES, "decrypt", aes_decrypt, 2);
75
+ rb_define_module_function(rb_mYubikeyCRC, "valid?", crc_check, 1);
76
+ }
@@ -0,0 +1,23 @@
1
+ class String
2
+
3
+ # Convert hex string to binary
4
+ def to_bin
5
+ to_a.pack('H*')
6
+ end
7
+
8
+ # Convert binary string to hex
9
+ def to_hex
10
+ unpack('H*')[0]
11
+ end
12
+
13
+ # Check if the string is hex encoded
14
+ def hex?
15
+ self =~ /^[0-9a-fA-F]+$/ ? true : false
16
+ end
17
+
18
+ # Check if the string is modhex encoded
19
+ def modhex?
20
+ self =~ /^[cbdefghijklnrtuv]+$/ ? true : false
21
+ end
22
+
23
+ end
@@ -0,0 +1,57 @@
1
+ class Yubikey::OTP
2
+ # first few modhex encoded characters of the OTP
3
+ attr_reader :public_id
4
+ # decrypted binary token
5
+ attr_reader :token
6
+ # binary AES key
7
+ attr_reader :aes_key
8
+ # hex id (encrypted in OTP)
9
+ attr_reader :secret_id
10
+ # integer that increments each time the Yubikey is plugged in
11
+ attr_reader :insert_counter
12
+ # ~8hz timer, reset on every insert
13
+ attr_reader :timestamp
14
+ # activation counter, reset on every insert
15
+ attr_reader :session_counter
16
+ # random integer used as padding and extra random noise
17
+ attr_reader :random_number
18
+
19
+
20
+ # Decode/decrypt a Yubikey one-time password
21
+ #
22
+ # [+otp+] ModHex encoded Yubikey OTP (at least 32 characters)
23
+ # [+key+] 32-character hex AES key
24
+ def initialize(otp, key)
25
+ raise InvalidOTPError, 'OTP must be at least 32 characters of modhex' unless otp.modhex? && otp.length >= 32
26
+ raise InvalidKeyError, 'Key must be 32 hex characters' unless key.hex? && key.length == 32
27
+
28
+ @public_id = otp[0,otp.length-32] if otp.length > 32
29
+
30
+ @token = Yubikey::ModHex.decode(otp[-32,32])
31
+ @aes_key = key.to_bin
32
+
33
+ decrypt
34
+ parse
35
+ end
36
+
37
+ private
38
+
39
+ def decrypt
40
+ @token = Yubikey::AES.decrypt(@token, @aes_key)
41
+ end
42
+
43
+ def parse
44
+ raise BadCRCError unless Yubikey::CRC.valid?(@token)
45
+
46
+ @secret_id = @token[0,6].to_hex
47
+ @insert_counter = @token[7] * 256 + @token[6]
48
+ @timestamp = @token[10] * 65536 + @token[9] * 256 + @token[8]
49
+ @session_counter = @token[11]
50
+ @random_number = @token[13] * 256 + @token[12]
51
+ end
52
+
53
+ # :stopdoc:
54
+ class InvalidOTPError < StandardError; end
55
+ class InvalidKeyError < StandardError; end
56
+ class BadCRCError < StandardError; end
57
+ end # Yubikey::OTP
data/lib/yubikey.rb ADDED
@@ -0,0 +1,6 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ require 'yubikey_ext'
5
+ require 'yubikey/hex'
6
+ require 'yubikey/otp'
data/spec/hex_spec.rb ADDED
@@ -0,0 +1,27 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ describe 'hex' do
4
+ it 'should encode binary to hex' do
5
+ "i\266H\034\213\253\242\266\016\217\"\027\233X\315V".to_hex.
6
+ should == '69b6481c8baba2b60e8f22179b58cd56'
7
+
8
+ "\354\336\030\333\347o\275\f33\017\0345Hq\333".to_hex.
9
+ should == 'ecde18dbe76fbd0c33330f1c354871db'
10
+ end
11
+
12
+ it 'should decode hex to binary' do
13
+ '69b6481c8baba2b60e8f22179b58cd56'.to_bin.
14
+ should == "i\266H\034\213\253\242\266\016\217\"\027\233X\315V"
15
+
16
+ 'ecde18dbe76fbd0c33330f1c354871db'.to_bin.
17
+ should == "\354\336\030\333\347o\275\f33\017\0345Hq\333"
18
+ end
19
+
20
+ it 'should know whether a string is hex' do
21
+ 'ecde18dbe76fbd0c33330f1c354871db'.hex?.should == true
22
+ 'dteffujehknhfjbrjnlnldnhcujvddbikngjrtgh'.modhex?.should == true
23
+
24
+ 'foobar'.hex?.should == false
25
+ 'test'.modhex?.should == false
26
+ end
27
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1 @@
1
+ --colour
@@ -0,0 +1,10 @@
1
+ begin
2
+ require 'spec'
3
+ rescue LoadError
4
+ require 'rubygems'
5
+ gem 'rspec'
6
+ require 'spec'
7
+ end
8
+
9
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
10
+ require 'yubikey'
@@ -0,0 +1,48 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ $:.unshift File.dirname(__FILE__) + '/../ext/yubikey_ext'
4
+ require 'yubikey_ext.so'
5
+
6
+ describe 'yubikey_ext' do
7
+ it 'should decode modhex' do
8
+ Yubikey::ModHex.decode('hknhfjbrjnlnldnhcujvddbikngjrtgh').should == "i\266H\034\213\253\242\266\016\217\"\027\233X\315V"
9
+ Yubikey::ModHex.decode('urtubjtnuihvntcreeeecvbregfjibtn').should == "\354\336\030\333\347o\275\f33\017\0345Hq\333"
10
+
11
+ Yubikey::ModHex.decode('dteffuje').should == "-4N\203"
12
+
13
+ Yubikey::ModHex.decode('ifhgieif').should == 'test'
14
+ Yubikey::ModHex.decode('hhhvhvhdhbid').should == 'foobar'
15
+
16
+ Yubikey::ModHex.decode('cc').should == "\000"
17
+ end
18
+
19
+ it 'should raise if modhex string length uneven' do
20
+ lambda { Yubikey::ModHex.decode('ifh') }.should raise_error(ArgumentError)
21
+ end
22
+
23
+ it 'should decrypt aes' do
24
+ key = '72992427a3b8ccd20697493b5532561f'.to_bin
25
+ state = 'ddf43aec57366784e061a12f767e728a'.to_bin
26
+ plain = '619dd70df3b30300de1bdb00ffbf6f26'.to_bin
27
+
28
+ Yubikey::AES.decrypt(state, key).should == plain
29
+ end
30
+
31
+ it 'should raise if aes key or state length is not 16' do
32
+ lambda { Yubikey::AES.decrypt("i\266H\034\213\253\242\266\016\217\"\027\233X\315V", 'test') }.
33
+ should raise_error(ArgumentError)
34
+
35
+ lambda { Yubikey::AES.decrypt('test', "\354\336\030\333\347o\275\f33\017\0345Hq\333") }.
36
+ should raise_error(ArgumentError)
37
+ end
38
+
39
+ it 'should check a crc' do
40
+ Yubikey::CRC.valid?('619dd70df3b30300de1bdb00ffbf6f26'.to_bin).should == true
41
+ Yubikey::CRC.valid?('ddf43aec57366784e061a12f767e728a'.to_bin).should == false
42
+ end
43
+
44
+ it 'should raise if crc token length not 16' do
45
+ lambda { Yubikey::CRC.valid?('619dd70df3b30300de1bdb00ffbf6f'.to_bin) }.
46
+ should raise_error(ArgumentError)
47
+ end
48
+ end
@@ -0,0 +1,28 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ describe 'Yubikey::OTP' do
4
+ it 'should parse a otp' do
5
+ token = Yubikey::OTP.new('dteffujehknhfjbrjnlnldnhcujvddbikngjrtgh', 'ecde18dbe76fbd0c33330f1c354871db')
6
+
7
+ token.public_id.should == 'dteffuje'
8
+ token.secret_id.should == '8792ebfe26cc'
9
+ token.insert_counter.should == 19
10
+ token.session_counter.should == 17
11
+ token.timestamp.should == 49712
12
+ token.random_number.should == 40904
13
+ end
14
+
15
+ it 'should raise if key or otp invalid' do
16
+ otp = 'hknhfjbrjnlnldnhcujvddbikngjrtgh'
17
+ key = 'ecde18dbe76fbd0c33330f1c354871db'
18
+
19
+ lambda { Yubikey::OTP.new(key, key) }.should raise_error(Yubikey::OTP::InvalidOTPError)
20
+ lambda { Yubikey::OTP.new(otp, otp) }.should raise_error(Yubikey::OTP::InvalidKeyError)
21
+
22
+ lambda { Yubikey::OTP.new(otp[0,31], key) }.should raise_error(Yubikey::OTP::InvalidOTPError)
23
+ lambda { Yubikey::OTP.new(otp, key[0,31]) }.should raise_error(Yubikey::OTP::InvalidKeyError)
24
+
25
+ lambda { Yubikey::OTP.new(otp[1,31]+'d', key) }.should raise_error(Yubikey::OTP::BadCRCError)
26
+ end
27
+
28
+ end
metadata ADDED
@@ -0,0 +1,71 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: yubikey
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Jonathan Rudenberg
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-04-02 00:00:00 -04:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: A library to decode, decrypt and parse Yubikey one-time passwords.
17
+ email: jon335@gmail.com
18
+ executables: []
19
+
20
+ extensions:
21
+ - ext/yubikey_ext/extconf.rb
22
+ extra_rdoc_files:
23
+ - ext/yubikey_ext/yubikey_ext.c
24
+ - README.rdoc
25
+ - LICENSE
26
+ files:
27
+ - lib/yubikey.rb
28
+ - lib/yubikey/hex.rb
29
+ - lib/yubikey/otp.rb
30
+ - examples/otp.rb
31
+ - spec/hex_spec.rb
32
+ - spec/spec.opts
33
+ - spec/spec_helper.rb
34
+ - spec/yubikey_ext_spec.rb
35
+ - spec/yubikey_otp_spec.rb
36
+ - ext/yubikey_ext/ykaes.c
37
+ - ext/yubikey_ext/ykcrc.c
38
+ - ext/yubikey_ext/ykmodhex.c
39
+ - ext/yubikey_ext/yubikey_ext.c
40
+ - ext/yubikey_ext/yubikey.h
41
+ - ext/yubikey_ext/extconf.rb
42
+ - README.rdoc
43
+ - LICENSE
44
+ has_rdoc: true
45
+ homepage: http://github.com/titanous/yubikey
46
+ post_install_message:
47
+ rdoc_options:
48
+ - --charset=UTF-8
49
+ require_paths:
50
+ - lib
51
+ required_ruby_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: "0"
56
+ version:
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: "0"
62
+ version:
63
+ requirements: []
64
+
65
+ rubyforge_project: yubikey
66
+ rubygems_version: 1.3.1
67
+ signing_key:
68
+ specification_version: 2
69
+ summary: A library to decode, decrypt and parse Yubikey one-time passwords.
70
+ test_files: []
71
+