yubikey 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
+