/* * 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; /* Initialized to 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_MD_SIZE]; if (gpg_pw_locked (PW_ERR_PW1)) return 0; DEBUG_INFO ("verify_pso_cds\r\n"); DEBUG_BYTE (pw_len); sha1 (pw, pw_len, keystring); if ((r = gpg_do_load_prvkey (GPG_KEY_FOR_SIGNING, BY_USER, keystring)) < 0) { gpg_pw_increment_err_counter (PW_ERR_PW1); return -1; } else if (r == 0) /* No key is available. Fail even if password can match. */ return -1; gpg_pw_reset_err_counter (PW_ERR_PW1); auth_status |= AC_PSO_CDS_AUTHORIZED; return 1; } int verify_other (const uint8_t *pw, int pw_len) { int r1, r2; uint8_t keystring[KEYSTRING_MD_SIZE]; DEBUG_INFO ("verify_other\r\n"); if (gpg_pw_locked (PW_ERR_PW1)) return 0; sha1 (pw, pw_len, keystring); if ((r1 = gpg_do_load_prvkey (GPG_KEY_FOR_DECRYPTION, BY_USER, keystring)) < 0 || (r2 = gpg_do_load_prvkey (GPG_KEY_FOR_AUTHENTICATION, BY_USER, keystring)) < 0) { gpg_pw_increment_err_counter (PW_ERR_PW1); return -1; } else if (r1 == 0 && r2 == 0) /* No key is available. Fail even if password can match. */ return -1; gpg_pw_reset_err_counter (PW_ERR_PW1); auth_status |= AC_OTHER_AUTHORIZED; return 1; } /* * 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_pw_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_pw_increment_err_counter (PW_ERR_PW3); return -1; } admin_authorized = BY_ADMIN; success: /* OK, the user is now authenticated */ gpg_pw_reset_err_counter (PW_ERR_PW3); return pw_len; } else { const uint8_t *ks_pw1 = gpg_do_read_simple (NR_DO_KEYSTRING_PW1); if (ks_pw1 != NULL) { /* empty PW3, but PW1 exists */ int r; uint8_t keystring[KEYSTRING_MD_SIZE]; pw_len = ks_pw1[0]; if ((pw_len_known >= 0 && pw_len_known != pw_len) || buf_len < pw_len) goto failure; sha1 (pw, pw_len, keystring); if ((r = gpg_do_load_prvkey (GPG_KEY_FOR_SIGNING, BY_USER, keystring)) < 0) goto failure; else if (r > 0) { admin_authorized = BY_USER; goto success; } /* if r == 0 (no signing key), then fall through */ } /* * For the case of empty PW3 (with empty PW1 or no signing key yet), * pass phrase should be OPENPGP_CARD_INITIAL_PW3 */ pw_len = strlen (OPENPGP_CARD_INITIAL_PW3); if ((pw_len_known >=0 && pw_len_known != pw_len) || buf_len < pw_len || strncmp ((const char *)pw, OPENPGP_CARD_INITIAL_PW3, pw_len)) goto failure; admin_authorized = BY_ADMIN; 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; }