diff options
| author | David M. Syzdek <david@syzdek.net> | 2023-01-11 16:05:55 -0900 |
|---|---|---|
| committer | David M. Syzdek <david@syzdek.net> | 2023-01-11 16:06:27 -0900 |
| commit | 98bcaf0aeebbb68d51c4321c804d2092f0905487 (patch) | |
| tree | 50e76f5fd3107e04d2383443816d5949e177b3d5 /lib | |
| parent | 2a8a9f3e12968ab262cff5ac363daa9cb2822572 (diff) | |
| download | otputils-98bcaf0aeebbb68d51c4321c804d2092f0905487.tar.gz otputils-98bcaf0aeebbb68d51c4321c804d2092f0905487.tar.bz2 otputils-98bcaf0aeebbb68d51c4321c804d2092f0905487.tar.xz otputils-98bcaf0aeebbb68d51c4321c804d2092f0905487.zip | |
libotputil: adding support for six-word encoding in otputil_otp_decode()
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/libotputil/lrfc2289-otp.c | 294 |
1 files changed, 292 insertions, 2 deletions
diff --git a/lib/libotputil/lrfc2289-otp.c b/lib/libotputil/lrfc2289-otp.c index 627ed12..fe96ce9 100644 --- a/lib/libotputil/lrfc2289-otp.c +++ b/lib/libotputil/lrfc2289-otp.c | |||
| @@ -49,6 +49,7 @@ | |||
| 49 | #include <openssl/evp.h> | 49 | #include <openssl/evp.h> |
| 50 | #include <openssl/hmac.h> | 50 | #include <openssl/hmac.h> |
| 51 | 51 | ||
| 52 | #include "lmisc.h" | ||
| 52 | #include "lrfc1760-skey-dict.h" | 53 | #include "lrfc1760-skey-dict.h" |
| 53 | 54 | ||
| 54 | 55 | ||
| @@ -66,6 +67,33 @@ otputil_otp_decode_hex( | |||
| 66 | 67 | ||
| 67 | 68 | ||
| 68 | static int | 69 | static int |
| 70 | otputil_otp_decode_value( | ||
| 71 | int val, | ||
| 72 | int * wordcount, | ||
| 73 | otputil_bv_t * dst, | ||
| 74 | size_t maxlen ); | ||
| 75 | |||
| 76 | |||
| 77 | static int | ||
| 78 | otputil_otp_decode_verify( | ||
| 79 | int wordcount, | ||
| 80 | otputil_bv_t * dst ); | ||
| 81 | |||
| 82 | |||
| 83 | static int | ||
| 84 | otputil_otp_decode_word( | ||
| 85 | const char * word, | ||
| 86 | int * methodp, | ||
| 87 | const EVP_MD * evp_md ); | ||
| 88 | |||
| 89 | |||
| 90 | static int | ||
| 91 | otputil_otp_dict_value( | ||
| 92 | const char * word, | ||
| 93 | const EVP_MD * evp_md ); | ||
| 94 | |||
| 95 | |||
| 96 | static int | ||
| 69 | otputil_otp_encode_word( | 97 | otputil_otp_encode_word( |
| 70 | char * dst, | 98 | char * dst, |
| 71 | int value, | 99 | int value, |
| @@ -88,11 +116,21 @@ otputil_otp_encode_word( | |||
| 88 | otputil_bv_t * | 116 | otputil_bv_t * |
| 89 | otputil_otp_decode( | 117 | otputil_otp_decode( |
| 90 | const char * src, | 118 | const char * src, |
| 91 | otputil_bv_t * dst ) | 119 | otputil_bv_t * dst, |
| 120 | int altdict_hash ) | ||
| 92 | { | 121 | { |
| 93 | static otputil_bv_t bv; | 122 | static otputil_bv_t bv; |
| 94 | static uint8_t buff[OTPUTIL_MAX_DECODE_SIZE]; | 123 | static uint8_t buff[OTPUTIL_MAX_DECODE_SIZE]; |
| 124 | char word[OTPUTIL_MAX_WORD_SIZE]; | ||
| 125 | int wlen; | ||
| 95 | int rc; | 126 | int rc; |
| 127 | int method; | ||
| 128 | int pos; | ||
| 129 | int len; | ||
| 130 | int val; | ||
| 131 | int wordcount; | ||
| 132 | size_t bv_len; | ||
| 133 | const EVP_MD * evp_md; | ||
| 96 | 134 | ||
| 97 | assert(src != NULL); | 135 | assert(src != NULL); |
| 98 | 136 | ||
| @@ -110,9 +148,49 @@ otputil_otp_decode( | |||
| 110 | if (rc == -1) | 148 | if (rc == -1) |
| 111 | return(NULL); | 149 | return(NULL); |
| 112 | 150 | ||
| 151 | // determine message digest | ||
| 152 | evp_md = (!(altdict_hash)) ? NULL : otputil_evp_md(altdict_hash); | ||
| 153 | if ( ((altdict_hash)) && (!(evp_md)) ) | ||
| 154 | return(NULL); | ||
| 155 | |||
| 113 | // checks for six-word format with S/KEY dictionary | 156 | // checks for six-word format with S/KEY dictionary |
| 157 | method = 0; | ||
| 158 | len = -1; | ||
| 159 | bv_len = dst->bv_len; | ||
| 160 | dst->bv_len = 0; | ||
| 161 | wordcount = 0; | ||
| 162 | for(pos = 0; ((src[pos])); pos++) | ||
| 163 | { | ||
| 164 | // skip white space | ||
| 165 | if ((isspace(src[pos]))) | ||
| 166 | continue; | ||
| 167 | |||
| 168 | // check for invalid character | ||
| 169 | if (!(isalnum(src[pos]))) | ||
| 170 | return(NULL); | ||
| 171 | |||
| 172 | // process word | ||
| 173 | for(wlen = 0; ( ((isalnum(src[pos+wlen])) && (wlen < (int)sizeof(word))) ); wlen++) | ||
| 174 | word[wlen] = src[pos+wlen]; | ||
| 175 | if (wlen >= (int)sizeof(word)) | ||
| 176 | return(NULL); | ||
| 177 | word[wlen] = '\0'; | ||
| 178 | pos += wlen - 1; | ||
| 179 | |||
| 180 | // decode word | ||
| 181 | if ((val = otputil_otp_decode_word(word, &method, evp_md)) == -1) | ||
| 182 | return(NULL); | ||
| 114 | 183 | ||
| 115 | return(NULL); | 184 | // add value to buffer |
| 185 | if (otputil_otp_decode_value(val, &wordcount, dst, bv_len) == -1) | ||
| 186 | return(NULL); | ||
| 187 | }; | ||
| 188 | |||
| 189 | // verify checksum of six-word encoding | ||
| 190 | if (otputil_otp_decode_verify(wordcount, dst) == -1) | ||
| 191 | return(NULL); | ||
| 192 | |||
| 193 | return(dst); | ||
| 116 | } | 194 | } |
| 117 | 195 | ||
| 118 | 196 | ||
| @@ -165,6 +243,194 @@ otputil_otp_decode_hex( | |||
| 165 | return(0); | 243 | return(0); |
| 166 | } | 244 | } |
| 167 | 245 | ||
| 246 | |||
| 247 | int | ||
| 248 | otputil_otp_decode_value( | ||
| 249 | int val, | ||
| 250 | int * wordcount, | ||
| 251 | otputil_bv_t * dst, | ||
| 252 | size_t maxlen ) | ||
| 253 | { | ||
| 254 | int8_t * bytes; | ||
| 255 | |||
| 256 | if ((val & ~0x07FF)) | ||
| 257 | return(-1); | ||
| 258 | if ((dst->bv_len+2) > maxlen) | ||
| 259 | return(-1); | ||
| 260 | |||
| 261 | bytes = dst->bv_val; | ||
| 262 | |||
| 263 | switch(*wordcount % 8) | ||
| 264 | { | ||
| 265 | case 0: // word 0: ( 00000000 000----- -------- ) | ||
| 266 | bytes[dst->bv_len++] = (val >> 3) & 0xff; | ||
| 267 | bytes[dst->bv_len++] = (val << 5) & 0xe0; | ||
| 268 | break; | ||
| 269 | |||
| 270 | case 1: // word 1: ( 00011111 111111-- -------- ) | ||
| 271 | bytes[dst->bv_len-1] |= (val >> 6) & 0x1f; | ||
| 272 | bytes[dst->bv_len++] = (val << 2) & 0xfc; | ||
| 273 | break; | ||
| 274 | |||
| 275 | case 2: // word 2: ( 11111122 22222222 2------- ) | ||
| 276 | if ((dst->bv_len+2) > maxlen) | ||
| 277 | return(-1); | ||
| 278 | bytes[dst->bv_len-1] |= (val >> 9) & 0x03; | ||
| 279 | bytes[dst->bv_len++] = (val >> 1) & 0xff; | ||
| 280 | bytes[dst->bv_len++] = (val << 7) & 0x80; | ||
| 281 | break; | ||
| 282 | |||
| 283 | case 3: // word 3: ( 23333333 3333---- -------- ) | ||
| 284 | bytes[dst->bv_len-1] |= (val >> 4) & 0x7f; | ||
| 285 | bytes[dst->bv_len++] = (val << 4) & 0xf0; | ||
| 286 | break; | ||
| 287 | |||
| 288 | case 4: // word 4: ( 33334444 4444444- -------- ) | ||
| 289 | bytes[dst->bv_len-1] |= (val >> 7) & 0x0f; | ||
| 290 | bytes[dst->bv_len++] = (val << 1) & 0xfe; | ||
| 291 | break; | ||
| 292 | |||
| 293 | case 5: // word 5: ( 44444445 55555555 55------ ) | ||
| 294 | if ((dst->bv_len+2) > maxlen) | ||
| 295 | return(-1); | ||
| 296 | bytes[dst->bv_len-1] |= (val >> 10) & 0x01; | ||
| 297 | bytes[dst->bv_len++] = (val >> 2) & 0xff; | ||
| 298 | bytes[dst->bv_len++] = (val << 6) & 0xc0; | ||
| 299 | break; | ||
| 300 | |||
| 301 | case 6: // word 6: ( 55666666 66666--- -------- ) | ||
| 302 | bytes[dst->bv_len-1] |= (val >> 5) & 0x3f; | ||
| 303 | bytes[dst->bv_len++] = (val << 3) & 0xf8; | ||
| 304 | break; | ||
| 305 | |||
| 306 | case 7: // word 7: ( 66666777 77777777 -------- ) | ||
| 307 | bytes[dst->bv_len-1] |= (val >> 8) & 0xe0; | ||
| 308 | bytes[dst->bv_len++] = (val << 0) & 0x0f; | ||
| 309 | break; | ||
| 310 | |||
| 311 | default: | ||
| 312 | break; | ||
| 313 | }; | ||
| 314 | |||
| 315 | (*wordcount)++; | ||
| 316 | |||
| 317 | return(0); | ||
| 318 | } | ||
| 319 | |||
| 320 | |||
| 321 | int | ||
| 322 | otputil_otp_decode_verify( | ||
| 323 | int wordcount, | ||
| 324 | otputil_bv_t * dst ) | ||
| 325 | { | ||
| 326 | size_t pos; | ||
| 327 | int8_t * bytes; | ||
| 328 | int checksum; | ||
| 329 | int sum; | ||
| 330 | |||
| 331 | if (!(wordcount)) | ||
| 332 | return(0); | ||
| 333 | |||
| 334 | bytes = dst->bv_val; | ||
| 335 | |||
| 336 | // finish checksum | ||
| 337 | // Mappings: | ||
| 338 | // 0 1 2 3 4 5 6 7 8 9 a | ||
| 339 | // 00000000 00011111 11111122 22222222 23333333 33334444 44444445 55555555 55666666 66666777 77777777 | ||
| 340 | switch(wordcount % 8) | ||
| 341 | { | ||
| 342 | case 1: // word 0: bytes 1: ( 00000000 000----- -------- ) | ||
| 343 | checksum = (bytes[dst->bv_len-1] >> 6) & 0x03; | ||
| 344 | dst->bv_len--; | ||
| 345 | break; | ||
| 346 | |||
| 347 | case 2: // word 1: bytes 1: ( 00011111 111111-- -------- ) | ||
| 348 | return(-1); | ||
| 349 | |||
| 350 | case 3: // word 2: bytes 2: ( 11111122 22222222 2------- ) | ||
| 351 | checksum = (bytes[dst->bv_len-2] >> 6) & 0x03; | ||
| 352 | dst->bv_len -= 2; | ||
| 353 | break; | ||
| 354 | |||
| 355 | case 4: // word 3: bytes 3: ( 23333333 3333---- -------- ) | ||
| 356 | checksum = (bytes[dst->bv_len-1] >> 6) & 0x03; | ||
| 357 | dst->bv_len--; | ||
| 358 | break; | ||
| 359 | |||
| 360 | case 5: // word 4: bytes 4: ( 33334444 4444444- -------- ) | ||
| 361 | checksum = (bytes[dst->bv_len-1] >> 6) & 0x03; | ||
| 362 | dst->bv_len--; | ||
| 363 | break; | ||
| 364 | |||
| 365 | case 6: // word 5: bytes 5: ( 44444445 55555555 55------ ) | ||
| 366 | checksum = (bytes[dst->bv_len-1] >> 6) & 0x03; | ||
| 367 | dst->bv_len--; | ||
| 368 | break; | ||
| 369 | |||
| 370 | case 7: // word 6: bytes 6: ( 55666666 66666--- -------- ) | ||
| 371 | checksum = (bytes[dst->bv_len-1] >> 6) & 0x03; | ||
| 372 | dst->bv_len--; | ||
| 373 | break; | ||
| 374 | |||
| 375 | case 0: // word 7: bytes 7: ( 66666777 77777777 -------- ) | ||
| 376 | checksum = (bytes[dst->bv_len-1] >> 6) & 0x03; | ||
| 377 | dst->bv_len--; | ||
| 378 | break; | ||
| 379 | |||
| 380 | default: | ||
| 381 | return(-1); | ||
| 382 | }; | ||
| 383 | |||
| 384 | // start generating checksum | ||
| 385 | for(pos = 0, sum = 0; (pos < dst->bv_len); pos++) | ||
| 386 | { | ||
| 387 | sum += (bytes[pos] >> 6) & 0x03; | ||
| 388 | sum += (bytes[pos] >> 4) & 0x03; | ||
| 389 | sum += (bytes[pos] >> 2) & 0x03; | ||
| 390 | sum += (bytes[pos] >> 0) & 0x03; | ||
| 391 | sum &= 0x03; | ||
| 392 | }; | ||
| 393 | if (sum != checksum) | ||
| 394 | return(-1); | ||
| 395 | |||
| 396 | return(0); | ||
| 397 | } | ||
| 398 | |||
| 399 | |||
| 400 | int | ||
| 401 | otputil_otp_decode_word( | ||
| 402 | const char * word, | ||
| 403 | int * methodp, | ||
| 404 | const EVP_MD * evp_md ) | ||
| 405 | { | ||
| 406 | int val; | ||
| 407 | |||
| 408 | switch(*methodp) | ||
| 409 | { | ||
| 410 | case OTPUTIL_ENC_SIXWORD: | ||
| 411 | return(otputil_skey_dict_value(word)); | ||
| 412 | |||
| 413 | case OTPUTIL_ENC_ALTDICT: | ||
| 414 | return(otputil_otp_dict_value(word, evp_md)); | ||
| 415 | |||
| 416 | default: | ||
| 417 | if ((val = otputil_skey_dict_value(word)) != -1) | ||
| 418 | { | ||
| 419 | *methodp = OTPUTIL_ENC_SIXWORD; | ||
| 420 | return(val); | ||
| 421 | }; | ||
| 422 | if ((val = otputil_otp_dict_value(word, evp_md)) != -1) | ||
| 423 | { | ||
| 424 | *methodp = OTPUTIL_ENC_ALTDICT; | ||
| 425 | return(val); | ||
| 426 | }; | ||
| 427 | break; | ||
| 428 | }; | ||
| 429 | |||
| 430 | return(-1); | ||
| 431 | } | ||
| 432 | |||
| 433 | |||
| 168 | size_t | 434 | size_t |
| 169 | otputil_otp_decode_len( | 435 | otputil_otp_decode_len( |
| 170 | const char * str ) | 436 | const char * str ) |
| @@ -182,6 +448,30 @@ otputil_otp_decode_len( | |||
| 182 | } | 448 | } |
| 183 | 449 | ||
| 184 | 450 | ||
| 451 | int | ||
| 452 | otputil_otp_dict_value( | ||
| 453 | const char * word, | ||
| 454 | const EVP_MD * evp_md ) | ||
| 455 | { | ||
| 456 | unsigned char md[EVP_MAX_MD_SIZE]; | ||
| 457 | unsigned md_len; | ||
| 458 | int val; | ||
| 459 | |||
| 460 | if (!(evp_md)) | ||
| 461 | return(-1); | ||
| 462 | |||
| 463 | md_len = sizeof(md); | ||
| 464 | if (!(EVP_Digest(word, strlen(word), md, &md_len, evp_md, NULL))) | ||
| 465 | return(-1); | ||
| 466 | |||
| 467 | val = 0; | ||
| 468 | val |= (md[md_len-1] & 0xff) << 0; | ||
| 469 | val |= (md[md_len-2] & 0x07) << 8; | ||
| 470 | |||
| 471 | return(val); | ||
| 472 | } | ||
| 473 | |||
| 474 | |||
| 185 | const char * | 475 | const char * |
| 186 | otputil_otp_encode( | 476 | otputil_otp_encode( |
| 187 | const otputil_bv_t * bv, | 477 | const otputil_bv_t * bv, |
