/* * ac.c -- Check access condition * * Copyright (C) 2010 Free Software Initiative of Japan * Author: NIIBE Yutaka * * This file is a part of Gnuk, a GnuPG USB Token implementation. * * Gnuk is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Gnuk is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "config.h" #include "ch.h" #include "gnuk.h" #include "polarssl/config.h" #include "polarssl/sha1.h" uint8_t volatile auth_status = AC_NONE_AUTHORIZED; int ac_check_status (uint8_t ac_flag) { if (ac_flag == AC_ALWAYS) return 1; else if (ac_flag == AC_NEVER) return 0; else return (ac_flag & auth_status)? 1 : 0; } void ac_reset_pso_cds (void) { gpg_do_clear_prvkey (GPG_KEY_FOR_SIGNING); auth_status &= ~AC_PSO_CDS_AUTHORIZED; } void ac_reset_other (void) { gpg_do_clear_prvkey (GPG_KEY_FOR_DECRYPTION); gpg_do_clear_prvkey (GPG_KEY_FOR_AUTHENTICATION); auth_status &= ~AC_OTHER_AUTHORIZED; } /* * Verify for "Perform Security Operation : Compute Digital Signature" */ int verify_pso_cds (const uint8_t *pw, int pw_len) { int r; uint8_t keystring[KEYSTRING_SIZE_PW1]; if (gpg_passwd_locked (PW_ERR_PW1)) return 0; DEBUG_INFO ("verify_pso_cds\r\n"); DEBUG_BYTE (pw_len); keystring[0] = pw_len; sha1 (pw, pw_len, keystring+1); if ((r = gpg_do_load_prvkey (GPG_KEY_FOR_SIGNING, BY_USER, keystring+1)) < 0) { gpg_increment_pw_err_counter (PW_ERR_PW1); return r; } else gpg_reset_pw_err_counter (PW_ERR_PW1); auth_status |= AC_PSO_CDS_AUTHORIZED; return 1; } int verify_other (const uint8_t *pw, int pw_len) { const uint8_t *ks_pw1; uint8_t pw1_keystring[KEYSTRING_SIZE_PW1]; DEBUG_INFO ("verify_other\r\n"); if (gpg_passwd_locked (PW_ERR_PW1)) return 0; ks_pw1 = gpg_do_read_simple (NR_DO_KEYSTRING_PW1); if ((ks_pw1 == NULL && pw_len == strlen (OPENPGP_CARD_INITIAL_PW1)) || (ks_pw1 != NULL && pw_len == ks_pw1[0])) { /* No problem */ pw1_keystring[0] = pw_len; sha1 (pw, pw_len, pw1_keystring+1); if (gpg_do_load_prvkey (GPG_KEY_FOR_DECRYPTION, BY_USER, pw1_keystring + 1) < 0) goto error; if (gpg_do_load_prvkey (GPG_KEY_FOR_AUTHENTICATION, BY_USER, pw1_keystring + 1) < 0) goto error; /* Reset counter as it's success now */ gpg_reset_pw_err_counter (PW_ERR_PW1); auth_status |= AC_OTHER_AUTHORIZED; return 1; } else { error: gpg_increment_pw_err_counter (PW_ERR_PW1); return 0; } } /* * For keystring of PW3, we use SALT+ITER+MD format */ static uint32_t decode_iterate_count (uint8_t x) { return (16UL + ((x) & 15)) << (((x) >> 4) + 6); } static void calc_md (int count, const uint8_t *salt, const uint8_t *pw, int pw_len, uint8_t md[KEYSTRING_MD_SIZE]) { sha1_context sha1_ctx; sha1_starts (&sha1_ctx); while (count > pw_len + 8) { sha1_update (&sha1_ctx, salt, 8); sha1_update (&sha1_ctx, pw, pw_len); count -= pw_len + 8; } if (count < 8) sha1_update (&sha1_ctx, salt, count); else { sha1_update (&sha1_ctx, salt, 8); count -= 8; sha1_update (&sha1_ctx, pw, count); } sha1_finish (&sha1_ctx, md); memset (&sha1_ctx, 0, sizeof (sha1_ctx)); } uint8_t keystring_md_pw3[KEYSTRING_MD_SIZE]; uint8_t admin_authorized; int verify_admin_0 (const uint8_t *pw, int buf_len, int pw_len_known) { const uint8_t *pw3_keystring; int pw_len; if (gpg_passwd_locked (PW_ERR_PW3)) return 0; pw3_keystring = gpg_do_read_simple (NR_DO_KEYSTRING_PW3); if (pw3_keystring != NULL) { int count; uint8_t md[KEYSTRING_MD_SIZE]; const uint8_t *salt; pw_len = pw3_keystring[0]; if ((pw_len_known >= 0 && pw_len_known != pw_len) || pw_len < buf_len) goto failure; salt = &pw3_keystring[1]; count = decode_iterate_count (pw3_keystring[1+8]); calc_md (count, salt, pw, pw_len, md); if (memcmp (md, &pw3_keystring[1+8+1], KEYSTRING_MD_SIZE) != 0) { failure: gpg_increment_pw_err_counter (PW_ERR_PW3); return -1; } admin_authorized = BY_ADMIN; success: /* OK, the user is now authenticated */ gpg_reset_pw_err_counter (PW_ERR_PW3); return pw_len; } else { const uint8_t *ks_pw1; uint8_t pw1_keystring[KEYSTRING_SIZE_PW1]; ks_pw1 = gpg_do_read_simple (NR_DO_KEYSTRING_PW1); if (ks_pw1 == NULL) { /* * For empty PW3 with empty PW1, pass phrase should be * OPENPGP_CARD_INITIAL_PW3 */ if ((pw_len_known >=0 && pw_len_known != strlen (OPENPGP_CARD_INITIAL_PW3)) || buf_len < (int)strlen (OPENPGP_CARD_INITIAL_PW3) || strncmp ((const char *)pw, OPENPGP_CARD_INITIAL_PW3, strlen (OPENPGP_CARD_INITIAL_PW3)) != 0) goto failure; pw_len = strlen (OPENPGP_CARD_INITIAL_PW3); admin_authorized = BY_ADMIN; goto success; } else /* empty PW3, but PW1 exists */ { pw_len = ks_pw1[0]; if (pw_len_known < 0 && pw_len_known != pw_len) goto failure; pw1_keystring[0] = pw_len; sha1 (pw, pw_len, pw1_keystring+1); if (gpg_do_load_prvkey (GPG_KEY_FOR_SIGNING, BY_USER, pw1_keystring + 1) < 0) goto failure; admin_authorized = BY_USER; goto success; } } } void gpg_set_pw3 (const uint8_t *newpw, int newpw_len) { uint8_t ks[KEYSTRING_SIZE_PW3]; uint32_t random; ks[0] = newpw_len; random = get_random (); memcpy (&ks[1], &random, sizeof (random)); random = get_random (); memcpy (&ks[5], &random, sizeof (random)); ks[9] = 0x60; /* 65536 iterations */ calc_md (65536, &ks[1], newpw, newpw_len, &ks[10]); gpg_do_write_simple (NR_DO_KEYSTRING_PW3, ks, KEYSTRING_SIZE_PW3); } int verify_admin (const uint8_t *pw, int pw_len) { int r; r = verify_admin_0 (pw, pw_len, pw_len); if (r <= 0) return r; sha1 (pw, pw_len, keystring_md_pw3); auth_status |= AC_ADMIN_AUTHORIZED; return 1; } void ac_reset_admin (void) { memset (keystring_md_pw3, 0, KEYSTRING_MD_SIZE); auth_status &= ~AC_ADMIN_AUTHORIZED; } void ac_fini (void) { memset (keystring_md_pw3, 0, KEYSTRING_MD_SIZE); gpg_do_clear_prvkey (GPG_KEY_FOR_SIGNING); gpg_do_clear_prvkey (GPG_KEY_FOR_DECRYPTION); gpg_do_clear_prvkey (GPG_KEY_FOR_AUTHENTICATION); auth_status = AC_NONE_AUTHORIZED; }