├── Makefile ├── NOTES.txt ├── Readme.txt ├── auth-loop.sh ├── ca.crt ├── createlogDB.sh ├── ecdsa-pkcs11-to-asn1.c ├── entropy.bin ├── logDB.sqlite ├── opensc.conf ├── piv-auth.c ├── simple-card-auth.service └── simple-card-auth.sh /Makefile: -------------------------------------------------------------------------------- 1 | 2 | 3 | CFLAGS=-g -O0 -Wno-deprecated-declarations 4 | 5 | LDFLAGS=-lcrypto 6 | 7 | all: ecdsa-pkcs11-to-asn1 8 | 9 | 10 | clean: 11 | $(RM) ecdsa-pkcs11-to-asn1 12 | -------------------------------------------------------------------------------- /NOTES.txt: -------------------------------------------------------------------------------- 1 | # Simple PKI Physical Access Control # 2 | 3 | WARNING: The following notes are largely stream-of-consciousness, intended 4 | to just make sense to me. You've been warned! 5 | 6 | LDAP-based physical access control requires a sort of "cryptographic 7 | group". Normal LDAP user groups have no cryptographic assurances that 8 | the given users are actually authorized to be in that group aside from 9 | whatever security measures are already in place to ensure that the 10 | directory is not tampered with by unauthorized persons. 11 | 12 | Each group would have it's own private key (and presumably a 13 | certificate for that key). Provable group membership can be 14 | accomplished with by signing "access attestations". These attestations 15 | specify: 16 | 17 | * The DN of the user(or subgroup) in the group 18 | * The start-date that membership is granted 19 | * the expiration date after which membership has ended. 20 | * Additional access rules, like: 21 | * "Only valid every other thursday starting on the 5th of feb, 22 | 2015\." 23 | * "Only valid between the hours of 9am and 5pm" 24 | 25 | Multiple such attestations can be added, allowing for exceptions. For 26 | example, if the cleaning service calls up and says that they can't 27 | make it on wednesday but can make it on thursday, you can add another 28 | access attestation good only for that day(and update the previous 29 | attestation to start the wednesday after) 30 | 31 | Explicit revocations of attestations are handled by a separate 32 | mechanism. 33 | 34 | Individual access control points are each configured with 35 | 36 | * a list of acceptable certificate authorities 37 | * One or more certificates for the given access zone 38 | * A server (LDAP?) for looking up attestations and 39 | revocations. 40 | 41 | Revocations are exceptions. A revocation is necessary whenever the 42 | access capabilities of an attestation are reduced. If you are 43 | extending the expiration date of an attestation, then a revocation is 44 | not necessary. If you want to deny access for a particular person to a 45 | zone for the next week, then you will need to revoke the current 46 | attestation and issue a new attestation with a start date of a week 47 | from now. Revocations must be kept for as long as the attestation it 48 | revokes would otherwise be valid. For example, when the original 49 | attestation expires, the revocation may be discarded. This is to 50 | prevent previously revoked attestations from being reintroduced. 51 | 52 | Access points may have more than one zone certificate. For example, 53 | there may be a backyard zone, an indoor zone, and a secure-closet 54 | zone. The indoor access point would have both the indoor certificate 55 | and the secure-closet certificate, because access to the secure closet 56 | implies indoor access. The secure-closet access point would have only 57 | the secure closet zone. 58 | 59 | Attestations may indicate the number of factors required of the 60 | authentication method. For example, access may require just the 61 | credential or require the credential and a pin number. 62 | 63 | Credentials may be PKI-based secure-elements or HOTP/TOTP values. HOTP 64 | values may be printed out and used access. 65 | 66 | The flow looks like this: 67 | 68 | 1. User presents credential 69 | 2. Credential is verified -> DN of credential is obtained. If the 70 | credential is invalid (expired, bad signature, unknown CA), DENY 71 | ACCESS. 72 | 3. If DN is not already in `cn=users,cd=example,cd=com`, perform an 73 | LDAP lookup of the credential DN to see if there is an associated 74 | user DN. If so, assume that DN. If not, DENY ACCESS. 75 | 4. Check local access attestation cache for an attestation cert. If 76 | there isn't one, do an LDAP query to see if there are any. If not, 77 | DENY ACCESS. Add attestations to a access consideration list. 78 | 5. Verify all attestations (signature, expiration, start date, 79 | hours-of-access, etc.). Remove attestations from consideration 80 | that are not valid. 81 | 6. Check for attestation revocations. Remove any attestations from 82 | consideration AND from the cache that are revoked. Do a quick LDAP 83 | lookup for any pending revocations. 84 | 7. If there is at least one valid attestation in the consideration 85 | list, GRANT ACCESS. Otherwise, DENY ACCESS. 86 | 87 | Relevant RFCs: 88 | 89 | * RFC1272: X.500 and Domains 90 | * RFC2253: LDAP UTF-8 String Representation of Distinguished Names 91 | 92 | ## Attestations ## 93 | 94 | An attestation is a JSON dictionary (in [canonical 95 | format](https://github.com/mirkokiefer/canonical-json)) with an 96 | appended raw signature. 97 | 98 | The values in the dictionary are: 99 | 100 | * "type": [REQUIRED] Set to the string "attestation" 101 | * "version": [REQUIRED] Set to the string "1" 102 | * "userDN": [REQUIRED] the DN of the entity, in LDAP (RFC2253) 103 | format. 104 | * "zoneDN" : [REQUIRED] The DN of the zone that access is being 105 | granted to. The key which made the signature for the attestation 106 | must have a certificate for this zone. 107 | * "startDate": [REQUIRED] The effective start date, in ISO 8601 108 | format. 109 | * "endDate": [REQUIRED] The effective end date, in ISO 8601 format. 110 | * "issueDate": The date that the attestation was issued, in ISO 8601 111 | format. 112 | * "???": Some sort of TBD data describing hourly access attributes. 113 | 114 | Attestations are identified by the SHA-256 hash of the JSON part only. 115 | 116 | ## Revocations ## 117 | 118 | Much like an attestation, a revocation is a JSON dictionary (in 119 | [canonical format](https://github.com/mirkokiefer/canonical-json)) 120 | with an appended raw signature. 121 | 122 | The values in the dictionary are: 123 | 124 | * "type": [REQUIRED] Set to the string "revocation" 125 | * "version": [REQUIRED] Set to the string "1" 126 | * "hash": [REQUIRED] The SHA-256 hash of the JSON-part of the 127 | attestation (does not include signature!) 128 | * "zoneDN" : [REQUIRED] This must be set to the same value of the 129 | associated field in the attestation. 130 | * "issueDate": The date that the revocation was issued, in ISO 8601 131 | format. 132 | 133 | Attestations are identified by the SHA-256 hash of the JSON part only. 134 | 135 | ## More notes ## 136 | 137 | JSON can be parsed in shell scripts using: 138 | http://stedolan.github.io/jq/ 139 | 140 | ## RPI Installation ## 141 | 142 | sudo apt-get install libusb-dev libusb++-0.1-4c2 143 | sudo apt-get install libccid 144 | 145 | sudo apt-get install pcscd 146 | 147 | sudo apt-get install libpcsclite1 148 | sudo apt-get install libpcsclite-dev 149 | sudo apt-get install pcsc-tools 150 | sudo apt-get install libpcsc-perl 151 | 152 | sudo modprobe -r pn533 153 | sudo modprobe -r nfc 154 | 155 | sudo apt-get install libssl-dev 156 | sudo apt-get install libreadline-dev 157 | 158 | sudo apt-get install coolkey pcscd pcsc-tools pkg-config libpam-pkcs11 opensc libengine-pkcs11-openssl 159 | 160 | 161 | -------------------------------------------------------------------------------- /Readme.txt: -------------------------------------------------------------------------------- 1 | In order to install SimpleCardAuth on the RaspberryPi execute the following commands: 2 | ## RPI Installation ## 3 | 4 | sudo apt-get install libusb-dev libusb++-0.1-4c2 5 | sudo apt-get install libccid 6 | 7 | sudo apt-get install pcscd 8 | 9 | sudo apt-get install libpcsclite1 10 | sudo apt-get install libpcsclite-dev 11 | sudo apt-get install pcsc-tools 12 | sudo apt-get install libpcsc-perl 13 | 14 | sudo modprobe -r pn533 15 | sudo modprobe -r nfc 16 | 17 | sudo apt-get install libssl-dev 18 | sudo apt-get install libreadline-dev 19 | 20 | sudo apt-get install coolkey pcscd pcsc-tools pkg-config libpam-pkcs11 opensc libengine-pkcs11-openssl 21 | 22 | sudo apt-get install sqlite3 23 | 24 | #execute the following inside the "SimpleCardAuth" folder (or use the makefile alternatively) 25 | gcc ecdsa-pkcs11-to-asn1.c -lcrypto -o ecdsa-pkcs11-to-asn1 26 | 27 | ## Creating and revoking certificates ## 28 | XCA is a tool with GUI support that allows management of certificates without knowledge about the shell. 29 | So certificate management may be done by people with less technical background. 30 | 31 | Official homepage: http://xca.hohnstaedt.de/ 32 | XCA download: http://xca.hohnstaedt.de/index.php/download 33 | 34 | 1. In order to install XCA execute the following steps: 35 | sudo apt-get install libtool 36 | sudo apt-get install libqt4-core libqt4-gui 37 | sudo apt-get install libqt4-dev 38 | 2. download XCA according to http://xca.hohnstaedt.de/index.php/download 39 | 3. sudo ./configure; make -j6; sudo make install 40 | -------------------------------------------------------------------------------- /auth-loop.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | GPIO_STRIKE_PIN=17 4 | GPIO_STRIKE_PATH=/sys/class/gpio/gpio$GPIO_STRIKE_PIN 5 | 6 | GPIO_DB_OPEN_PIN=27 7 | GPIO_DB_OPEN_PATH=/sys/class/gpio/gpio$GPIO_DB_OPEN_PIN 8 | 9 | GPIO_DB_CLOSE_PIN=22 10 | GPIO_DB_CLOSE_PATH=/sys/class/gpio/gpio$GPIO_DB_CLOSE_PIN 11 | 12 | cd "`dirname $0`" 13 | 14 | init_door() { 15 | echo $GPIO_STRIKE_PIN > /sys/class/gpio/export 16 | echo $GPIO_DB_OPEN_PIN > /sys/class/gpio/export 17 | echo in > $GPIO_DB_OPEN_PATH/direction 18 | echo $GPIO_DB_CLOSE_PIN > /sys/class/gpio/export 19 | echo in > $GPIO_DB_CLOSE_PATH/direction 20 | } 21 | 22 | unlock_door() { 23 | echo out > $GPIO_STRIKE_PATH/direction 24 | echo 0 > $GPIO_STRIKE_PATH/value 25 | 26 | ( 27 | echo out > $GPIO_DB_OPEN_PATH/direction 28 | echo 1 > $GPIO_DB_OPEN_PATH/value 29 | sleep 1 30 | echo in > $GPIO_DB_OPEN_PATH/direction 31 | ) & 32 | } 33 | 34 | lock_door() { 35 | echo in > $GPIO_STRIKE_PATH/direction 36 | } 37 | 38 | access_denied() { 39 | echo Access Denied: $AUTH_DN 40 | 41 | # TODO: Log the incident. 42 | 43 | # Disable starting beep 44 | opensc-tool --send-apdu FF:00:52:00:00 > /dev/null 2> /dev/null 45 | 46 | # Beep three times with red LED to indicate failure 47 | # opensc-tool --send-apdu FF:00:40:5D:04:01:01:03:01 > /dev/null 2> /dev/null 48 | # sleep 1 49 | } 50 | 51 | access_granted() { 52 | echo Access Granted: $AUTH_DN 53 | 54 | unlock_door 55 | 56 | # Beep once with green LED to indicate success 57 | opensc-tool --send-apdu FF:00:40:2E:04:01:01:01:01 > /dev/null 2> /dev/null 58 | 59 | # Disable starting beep 60 | opensc-tool --send-apdu FF:00:52:00:00 > /dev/null 2> /dev/null 61 | 62 | sleep 4 63 | 64 | lock_door 65 | } 66 | 67 | verify_access() { 68 | # TODO: Look up and verify that $AUTH_DN has access to this zone! 69 | true 70 | } 71 | 72 | init_door 73 | 74 | echo Starting Auth Loop 75 | 76 | # Main access control loop 77 | while true ; 78 | do 79 | lock_door 80 | if AUTH_DN=`./simple-card-auth.sh` 81 | then if verify_access 82 | then access_granted 83 | else access_denied 84 | fi 85 | else access_denied 86 | fi 87 | done 88 | -------------------------------------------------------------------------------- /ca.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICijCCAjKgAwIBAgIBATAJBgcqhkjOPQQBMIGVMQswCQYDVQQGEwJERTEMMAoG 3 | A1UECBMDQmF5MRMwEQYDVQQHEwpSZWdlbnNidXJnMRQwEgYDVQQKDAtyb290X2Nh 4 | X29yZzEZMBcGA1UECwwQcm9vdF9jYV9vcmdfdW5pdDEQMA4GA1UEAwwHcm9vdF9j 5 | YTEgMB4GCSqGSIb3DQEJARYRc2VjcmV0QHJvb3RfY2EuZGUwHhcNMTYwODExMTMx 6 | NjAwWhcNMjYwODExMTMxNjAwWjCBlTELMAkGA1UEBhMCREUxDDAKBgNVBAgTA0Jh 7 | eTETMBEGA1UEBxMKUmVnZW5zYnVyZzEUMBIGA1UECgwLcm9vdF9jYV9vcmcxGTAX 8 | BgNVBAsMEHJvb3RfY2Ffb3JnX3VuaXQxEDAOBgNVBAMMB3Jvb3RfY2ExIDAeBgkq 9 | hkiG9w0BCQEWEXNlY3JldEByb290X2NhLmRlMFkwEwYHKoZIzj0CAQYIKoZIzj0D 10 | AQcDQgAE7JI5moZxAtxu5YB5VVuzaDqASKC8kBBg7MAwUgpKjabpadUJoJPs4h3Z 11 | lupjqfTNcbvCVMUPY9Swjv4+OzSBN6NyMHAwDwYDVR0TAQH/BAUwAwEB/zAdBgNV 12 | HQ4EFgQUrzPZXieDIx8vLKSbDPu/zvN9ym4wCwYDVR0PBAQDAgEGMBEGCWCGSAGG 13 | +EIBAQQEAwIABzAeBglghkgBhvhCAQ0EERYPeGNhIGNlcnRpZmljYXRlMAkGByqG 14 | SM49BAEDRwAwRAIgdCHYLjnQK3qP0Ap0zBB24J39wDbnhFl3l4yqCvYolMoCIHfy 15 | KqLP0frOZHGVNXUgocaWKW+GKR1fygUD8CBFit49 16 | -----END CERTIFICATE----- 17 | -----BEGIN X509 CRL----- 18 | MIIBKzCB1AIBATAJBgcqhkjOPQQBMIGVMQswCQYDVQQGEwJERTEMMAoGA1UECBMD 19 | QmF5MRMwEQYDVQQHEwpSZWdlbnNidXJnMRQwEgYDVQQKDAtyb290X2NhX29yZzEZ 20 | MBcGA1UECwwQcm9vdF9jYV9vcmdfdW5pdDEQMA4GA1UEAwwHcm9vdF9jYTEgMB4G 21 | CSqGSIb3DQEJARYRc2VjcmV0QHJvb3RfY2EuZGUXDTE2MDgxMTEzNTMwMFoXDTIw 22 | MDgxMDEzNTMwMFqgDjAMMAoGA1UdFAQDAgEBMAkGByqGSM49BAEDRwAwRAIgIm8A 23 | QX7zuZ1K/f0MUdLKxygRG6GGCtQOtUhWpn/xVo0CIFZbtCWejX2y0OJRNMuN5gwL 24 | k5ktDjf1wMSlYbqV7XCB 25 | -----END X509 CRL----- 26 | -------------------------------------------------------------------------------- /createlogDB.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | sqlite3 ./logDB.sqlite "CREATE TABLE Identity (ID INTEGER PRIMARY KEY, CommonName TEXT, OrganizationName TEXT, StateOrProvinceName TEXT, CountryName TEXT, LocalityName TEXT, OrganizationalUnitName TEXT, email TEXT);" 4 | 5 | sqlite3 ./logDB.sqlite "Create table AccessAttempt (ID INTEGER PRIMARY KEY, Identity_ID TEXT, timestamp TEXT, success INTEGER, FOREIGN KEY(Identity_ID) REFERENCES Identity(ID));" 6 | -------------------------------------------------------------------------------- /ecdsa-pkcs11-to-asn1.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | 7 | int main(int argc, char * argv[]) 8 | { 9 | uint8_t pkcs11_sig[512]; 10 | int pkcs11_sig_len = 0; 11 | 12 | pkcs11_sig_len = fread((void*)pkcs11_sig, 1, sizeof(pkcs11_sig), stdin); 13 | 14 | //fprintf(stderr,"Raw ECDSA Signature is %d bytes, key is %d bit\n", pkcs11_sig_len, pkcs11_sig_len*4); 15 | 16 | int nLen; 17 | ECDSA_SIG * ecsig = NULL; 18 | unsigned char *p = NULL; 19 | int der_len; 20 | nLen = pkcs11_sig_len/2; 21 | ecsig = ECDSA_SIG_new(); 22 | ecsig->r = BN_bin2bn(pkcs11_sig, nLen, ecsig->r); 23 | ecsig->s = BN_bin2bn(pkcs11_sig + nLen, nLen, ecsig->s); 24 | der_len = i2d_ECDSA_SIG(ecsig, &p); 25 | //fprintf(stderr,"Writing OpenSSL ECDSA_SIG\n"); 26 | fwrite(p,1,der_len,stdout); 27 | free(p); 28 | ECDSA_SIG_free(ecsig); 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /entropy.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darconeous/SimpleCardAuth/e018b2b7705d773a9c19c2c67982f38344f0f73b/entropy.bin -------------------------------------------------------------------------------- /logDB.sqlite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darconeous/SimpleCardAuth/e018b2b7705d773a9c19c2c67982f38344f0f73b/logDB.sqlite -------------------------------------------------------------------------------- /opensc.conf: -------------------------------------------------------------------------------- 1 | # Configuration file for OpenSC 2 | # Example configuration file 3 | 4 | # NOTE: All key-value pairs must be terminated by a semicolon. 5 | 6 | # Default values for any application 7 | # These can be overridden by an application 8 | # specific configuration block. 9 | app default { 10 | # Amount of debug info to print 11 | # 12 | # A greater value means more debug info. 13 | # Default: 0 14 | # 15 | debug = 0; 16 | 17 | # The file to which debug output will be written 18 | # 19 | # Special values 'stdout' and 'stderr' are recognized. 20 | # Default: stderr 21 | # 22 | # debug_file = /tmp/opensc-debug.log 23 | 24 | # Re-open debug file (used in WIN32) 25 | # 26 | # In Windows, file handles can not be shared between DLL-s, 27 | # each DLL has a separate file handle table. 28 | # For that reason reopen debug file before every debug message. 29 | # 30 | # Default: true 31 | # reopen_debug_file = false; 32 | 33 | # PKCS#15 initialization / personalization 34 | # profiles directory for pkcs15-init. 35 | # Default: /usr/local/Cellar/opensc/0.14.0_1/share/opensc 36 | # 37 | # profile_dir = /usr/local/Cellar/opensc/0.14.0_1/share/opensc; 38 | 39 | # Paranoid memory allocation. 40 | # 41 | # If set to 'true', then refuse to continue when locking of non-pageable 42 | # memory fails. This can cause subtle failures but is more secure when 43 | # you have a swap disk. 44 | # Default: false 45 | # 46 | # paranoid_memory = false; 47 | 48 | # Enable default card driver 49 | # Default card driver is explicitely enabled for the 'opensc-explorer' and 'opensc-tool'. 50 | # 51 | # Default: false 52 | # enable_default_driver = true; 53 | 54 | # CT-API module configuration. 55 | reader_driver ctapi { 56 | # module /usr/local/Cellar/opensc/0.14.0_1/lib/libtowitoko.so { 57 | # CT-API ports: 58 | # 0..3 COM1..4 59 | # 4 Printer 60 | # 5 Modem 61 | # 6..7 LPT1..2 62 | # ports = 0; 63 | # } 64 | } 65 | 66 | # The following section shows definitions for PC/SC readers. 67 | reader_driver pcsc { 68 | # Limit command and response sizes. 69 | # Default: n/a 70 | # max_send_size = 255; 71 | # max_recv_size = 256; 72 | # 73 | # Connect to reader in exclusive mode? 74 | # Default: false 75 | # connect_exclusive = true; 76 | # 77 | # What to do when disconnecting from a card (SCardDisconnect) 78 | # Valid values: leave, reset, unpower. 79 | # Default: reset 80 | # disconnect_action = unpower; 81 | # 82 | # What to do at the end of a transaction (SCardEndTransaction) 83 | # Valid values: leave, reset, unpower. 84 | # Default: leave 85 | # transaction_end_action = reset; 86 | # 87 | # What to do when reconnection to a card (SCardReconnect) 88 | # Valid values: leave, reset, unpower. 89 | # Note that this affects only the internal reconnect (after a SCARD_W_RESET_CARD). 90 | # A forced reset via sc_reset() always does a full powerup. 91 | # Default: leave 92 | # reconnect_action = reset; 93 | # 94 | # Enable pinpad if detected (PC/SC v2.0.2 Part 10) 95 | # Default: true 96 | # enable_pinpad = false; 97 | # 98 | # Use specific pcsc provider. 99 | # Default: /System/Library/Frameworks/PCSC.framework/PCSC 100 | # provider_library = /System/Library/Frameworks/PCSC.framework/PCSC 101 | } 102 | 103 | # Options for OpenCT support 104 | reader_driver openct { 105 | # Virtual readers to allocate. 106 | # Default: 2 107 | # readers = 5; 108 | # 109 | # Limit command and response sizes. 110 | # Default: n/a 111 | # max_send_size = 255; 112 | # max_recv_size = 256; 113 | } 114 | 115 | # What card drivers to load at start-up 116 | # 117 | # A special value of 'internal' will load all 118 | # statically linked drivers. If an unknown (ie. not 119 | # internal) driver is supplied, a separate configuration 120 | # configuration block has to be written for the driver. 121 | # Default: internal 122 | # NOTE: When "internal" keyword is used, must be last entry 123 | # 124 | #card_drivers = PIV-II, acos5, default; 125 | card_drivers = PIV-II, acos5, default; 126 | 127 | # Card driver configuration blocks. 128 | 129 | # For card drivers loaded from an external shared library/DLL, 130 | # you need to specify the path name of the module 131 | # 132 | # card_driver customcos { 133 | # The location of the driver library 134 | # module = /usr/local/Cellar/opensc/0.14.0_1/lib/card_customcos.so; 135 | # } 136 | 137 | # Force using specific card driver 138 | # 139 | # If this option is present, OpenSC will use the supplied 140 | # driver with all inserted cards. 141 | # 142 | # Default: autodetect 143 | # 144 | force_card_driver = PIV-II; 145 | 146 | # In addition to the built-in list of known cards in the 147 | # card driver, you can configure a new card for the driver 148 | # using the card_atr block. The goal is to centralize 149 | # everything related to a certain card to card_atr. 150 | # 151 | # The supported internal card driver names can be retrieved 152 | # from the output of: 153 | # $ opensc-tool --list-drivers 154 | 155 | # Generic format: card_atr 156 | 157 | # New card entry for the flex card driver 158 | # card_atr 3b:f0:0d:ca:fe { 159 | # All parameters for the context are 160 | # optional unless specified otherwise. 161 | 162 | # Context: global, card driver 163 | # 164 | # ATR mask value 165 | # 166 | # The mask is logically AND'd with an 167 | # card ATR prior to comparison with the 168 | # ATR reference value above. Using mask 169 | # allows identifying and configuring 170 | # multiple ATRs as the same card model. 171 | # atrmask = "ff:ff:ff:ff:ff"; 172 | 173 | # Context: card driver 174 | # 175 | # Specify used card driver (REQUIRED). 176 | # 177 | # When enabled, overrides all possible 178 | # settings from the card drivers built-in 179 | # card configuration list. 180 | # driver = "flex"; 181 | 182 | # Set card name for card drivers that allows it. 183 | # name = "My CryptoFlex card"; 184 | 185 | # Card type as an integer value. 186 | # 187 | # Depending on card driver, this allows 188 | # tuning the behaviour of the card driver 189 | # for your card. 190 | # type = "2002"; 191 | 192 | # Card flags as an hex value. 193 | # Multiple values are OR'd together. 194 | # 195 | # Depending on card driver, this allows 196 | # fine-tuning the capabilities in 197 | # the card driver for your card. 198 | # 199 | # Optionally, some known parameters 200 | # can be specified as strings: 201 | # 202 | # rng - On-board random number source 203 | # 204 | # flags = "rng", "0x80000000"; 205 | 206 | # 207 | # Context: PKCS#15 emulation layer 208 | # 209 | # When using PKCS#15 emulation, force 210 | # the emulation driver for specific cards. 211 | # 212 | # Required for external drivers, but can 213 | # be used with built-in drivers, too. 214 | # pkcs15emu = "custom"; 215 | 216 | # 217 | # Context: reader driver 218 | # 219 | # Force protocol selection for specific cards. 220 | # Known parameters: t0, t1, raw 221 | # force_protocol = "t0"; 222 | # } 223 | 224 | # PIV cards need an entry similar to this one: 225 | # card_atr 3B:7D:96:00:00:80:31:80:65:B0:83:11:00:AC:83:00:90:00 { 226 | # name = "PIV-II"; 227 | # driver = "piv"; 228 | # } 229 | card_atr 3b:8c:80:01:59:75:62:69:6b:65:79:4e:45:4f:72:33:58 { 230 | name = "Yubikey NEO"; 231 | driver = "piv"; 232 | } 233 | 234 | 235 | # Estonian ID card and Micardo driver sometimes only play together with T=0 236 | # In theory only the 'cold' ATR should be specified, as T=0 will 237 | # be the preferred protocol once you boot it up with T=0, but be 238 | # paranoid. 239 | # 240 | # Warm ATR v1 241 | card_atr "3b:6e:00:ff:45:73:74:45:49:44:20:76:65:72:20:31:2e:30" { 242 | force_protocol = t0; 243 | } 244 | # Cold ATR v1 245 | card_atr "3b:fe:94:00:ff:80:b1:fa:45:1f:03:45:73:74:45:49:44:20:76:65:72:20:31:2e:30:43" { 246 | force_protocol = t0; 247 | } 248 | # Warm ATR v2 249 | card_atr "3b:5e:11:ff:45:73:74:45:49:44:20:76:65:72:20:31:2e:30" { 250 | force_protocol = t0; 251 | } 252 | # Cold ATR v2 253 | card_atr "3b:de:18:ff:c0:80:b1:fe:45:1f:03:45:73:74:45:49:44:20:76:65:72:20:31:2e:30:2b" { 254 | force_protocol = t0; 255 | } 256 | # Digi-ID cold ATR. The same card has the same warm ATR as "Cold ATR v1" above 257 | # The card is claimed to only support T=0 but in fact (sometimes) works with T=1, even if not advertised in ATR. 258 | card_atr "3b:6e:00:00:45:73:74:45:49:44:20:76:65:72:20:31:2e:30" { 259 | force_protocol = t0; 260 | } 261 | 262 | # D-Trust cards are also based on micardo and need T=0 for some reason 263 | card_atr "3b:ff:94:00:ff:80:b1:fe:45:1f:03:00:68:d2:76:00:00:28:ff:05:1e:31:80:00:90:00:23" { 264 | force_protocol = t0; 265 | } 266 | card_atr "3b:ff:11:00:ff:80:b1:fe:45:1f:03:00:68:d2:76:00:00:28:ff:05:1e:31:80:00:90:00:a6" { 267 | force_protocol = t0; 268 | } 269 | 270 | # Oberthur's AuthentIC v3.2.2 271 | card_atr "3B:DD:18:00:81:31:FE:45:80:F9:A0:00:00:00:77:01:00:70:0A:90:00:8B" { 272 | type = 11100; 273 | driver = authentic; 274 | name = "AuthentIC v3.1"; 275 | 276 | # Name of SM configuration sub-section 277 | # secure_messaging = local_authentic; 278 | } 279 | 280 | # IAS/ECC cards 281 | card_atr "3B:7F:96:00:00:00:31:B9:64:40:70:14:10:73:94:01:80:82:90:00" { 282 | type = 25001; 283 | driver = iasecc; 284 | name = "Gemalto MultiApp IAS/ECC v1.0.1"; 285 | secure_messaging = "local_gemalto_iam"; 286 | # secure_messaging = local_adele; 287 | md_read_only = false; 288 | md_supports_X509_enrollment = true; 289 | } 290 | card_atr "3B:7F:96:00:00:00:31:B8:64:40:70:14:10:73:94:01:80:82:90:00" { 291 | type = 25001; 292 | driver = iasecc; 293 | name = "Gemalto MultiApp IAS/ECC v1.0.1"; 294 | secure_messaging = "local_gemalto_iam"; 295 | md_read_only = false; 296 | md_supports_X509_enrollment = true; 297 | } 298 | #card_atr 3B:DD:18:00:81:31:FE:45:80:F9:A0:00:00:00:77:01:08:00:07:90:00:FE { 299 | # type = 25002; 300 | # driver = "iasecc"; 301 | # name = "Oberthur IAS/ECC v1.0.1"; 302 | # # No 'admin' application for this card -- no secure messaging 303 | #} 304 | #card_atr 3B:7F:18:00:00:00:31:B8:64:50:23:EC:C1:73:94:01:80:82:90:00 { 305 | # type = 25003; 306 | # driver = "iasecc"; 307 | # name = "Morpho YpsID S3 IAS/ECC"; 308 | # # secure_messaging = local_morpho_YpsID_S3; 309 | #} 310 | card_atr "3B:DF:18:FF:81:91:FE:1F:C3:00:31:B8:64:0C:01:EC:C1:73:94:01:80:82:90:00:B3" { 311 | type = 25004; 312 | driver = iasecc; 313 | name = "Amos IAS/ECC v1.0.1"; 314 | md_read_only = false; 315 | md_supports_X509_enrollment = true; 316 | secure_messaging = "local_amos"; 317 | } 318 | card_atr "3B:DC:18:FF:81:91:FE:1F:C3:80:73:C8:21:13:66:01:0B:03:52:00:05:38" { 319 | type = 25004; 320 | driver = iasecc; 321 | name = "Amos IAS/ECC v1.0.1"; 322 | md_read_only = false; 323 | md_supports_X509_enrollment = true; 324 | secure_messaging = "local_amos_eid"; 325 | } 326 | 327 | secure_messaging "local_authentic" { 328 | #path to ans name of external SM module 329 | #module_name = libsmm-local.so.3; 330 | #module_path = /usr/local/Cellar/opensc/0.14.0_1/lib; 331 | 332 | # specific data to tune the module initialization 333 | #module_data = "Here can be your SM module init data"; 334 | 335 | # SM mode: 336 | # 'transmit' -- in this mode the procedure to securize an APDU is called by the OpenSC general 337 | # APDU transmit procedure. 338 | # In this mode all APDUs, except the ones filtered by the card specific procedure, 339 | # are securized. 340 | # 'acl' -- in this mode APDU are securized only if needed by the ACLs of the command to be executed. 341 | # 342 | #mode = transmit; 343 | 344 | # SM type specific flags 345 | # flags = 0x78; # 0x78 -- level 3, channel 0 346 | 347 | # Default KMC of the GP Card Manager for the Oberthur's Java cards 348 | # kmc = "00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00"; 349 | } 350 | 351 | secure_messaging "local_gemalto_iam" { 352 | module_name = "libsmm-local.so.3"; 353 | module_path = "/usr/local/Cellar/opensc/0.14.0_1/lib"; 354 | #module_data = ""; 355 | type = acl; 356 | # transmit, acl 357 | 358 | ifd_serial = "11:22:33:44:55:66:77:88"; 359 | 360 | # Keyset values from IAM profiles of the Gemalto IAS/ECC cards 361 | keyset_02_enc = "RW_PRIV_ENC_TEST"; 362 | keyset_02_mac = "RW_PRIV_MAC_TEST"; 363 | 364 | keyset_E828BD080FD2504543432D654944_01_enc = "RO_ENC_TEST_KEY_"; 365 | keyset_E828BD080FD2504543432D654944_01_mac = "RO_MAC_TEST_KEY_"; 366 | 367 | keyset_E828BD080FD2504543432D654944_03_enc = "RW_PUBL_ENC_TEST"; 368 | keyset_E828BD080FD2504543432D654944_03_mac = "RW_PUBL_MAC_TEST"; 369 | } 370 | 371 | secure_messaging "local_amos" { 372 | module_name = "libsmm-local.so.3"; 373 | module_path = "/usr/local/Cellar/opensc/0.14.0_1/lib"; 374 | mode = acl; 375 | ifd_serial = "11:22:33:44:55:66:77:88"; 376 | keyset_02_enc = ENCROECHANTILLON; 377 | keyset_02_mac = MACROECHANTILLON; 378 | } 379 | 380 | secure_messaging "local_amos_eid" { 381 | module_name = "libsmm-local.so.3"; 382 | module_path = "/usr/local/Cellar/opensc/0.14.0_1/lib"; 383 | mode = acl; 384 | ifd_serial = "11:22:33:44:55:66:77:88"; 385 | keyset_E828BD080FD2504543432D654944_03_enc = "RW_PUBL_ENC_TEST"; 386 | keyset_E828BD080FD2504543432D654944_03_mac = "RW_PUBL_MAC_TEST"; 387 | } 388 | 389 | secure_messaging "local_adele" { 390 | module_name = "libsmm-local.so.3"; 391 | module_path = "/usr/local/Cellar/opensc/0.14.0_1/lib"; 392 | #module_data = ""; 393 | type = acl; 394 | # transmit, acl 395 | 396 | ifd_serial = "11:22:33:44:55:66:77:88"; 397 | 398 | # Keyset values from 'Adele' profiles of the IAS/ECC cards 399 | keyset_01_enc = EMENCECHANTILLON; 400 | keyset_01_mac = EMMACECHANTILLON; 401 | 402 | keyset_02_enc = AAENCECHANTILLON; 403 | keyset_02_mac = AAMACECHANTILLON; 404 | 405 | keyset_E828BD080FD2500000040301_02_enc = E2ENCECHANTILLON; 406 | keyset_E828BD080FD2500000040301_02_mac = E2MACECHANTILLON; 407 | 408 | keyset_D2500000044164E86C650101_02_enc = E1ENCECHANTILLON; 409 | keyset_D2500000044164E86C650101_02_mac = E1MACECHANTILLON; 410 | 411 | keyset_D2500000044164E86C650101_03_enc = SIENCECHANTILLON; 412 | keyset_D2500000044164E86C650101_03_mac = SIMACECHANTILLON; 413 | } 414 | 415 | # Below are the framework specific configuration blocks. 416 | 417 | # PKCS #15 418 | framework pkcs15 { 419 | # Whether to use the cache files in the user's 420 | # home directory. 421 | # 422 | # At the moment you have to 'teach' the card 423 | # to the system by running command: pkcs15-tool -L 424 | # 425 | # WARNING: Caching shouldn't be used in setuid root 426 | # applications. 427 | # Default: false 428 | use_file_caching = true; 429 | # 430 | # Use PIN caching? 431 | # Default: true 432 | use_pin_caching = false; 433 | # 434 | # How many times to use a PIN from cache before re-authenticating it? 435 | # Default: 10 436 | # pin_cache_counter = 3; 437 | # 438 | # Older PKCS#11 applications not supporting CKA_ALWAYS_AUTHENTICATE 439 | # may need to set this to get signatures to work with some cards. 440 | # Default: false 441 | # pin_cache_ignore_user_consent = true; 442 | # 443 | # Enable pkcs15 emulation. 444 | # Default: yes 445 | # enable_pkcs15_emulation = no; 446 | # 447 | # Prefer pkcs15 emulation code before 448 | # the normal pkcs15 processing. 449 | # Some cards (like esteid and pteid) work in emu-only mode, 450 | # and do not depend on this option. 451 | # 452 | # Default: no 453 | # try_emulation_first = yes; 454 | 455 | # Enable builtin emulators. 456 | # Default: yes 457 | # enable_builtin_emulation = no; 458 | # 459 | # List of the builtin pkcs15 emulators to test 460 | # Default: esteid, openpgp, tcos, starcert, itacns, infocamere, postecert, actalis, atrust-acos, gemsafeGPK, gemsafeV1, tccardos, PIV-II; 461 | # builtin_emulators = openpgp; 462 | 463 | # additional settings per driver 464 | # 465 | # For pkcs15 emulators loaded from an external shared 466 | # library/DLL, you need to specify the path name of the module 467 | # and customize the card_atr example above correctly. 468 | # 469 | # emulate custom { 470 | # The location of the driver library 471 | # module = /usr/local/Cellar/opensc/0.14.0_1/lib/p15emu_custom.so; 472 | # } 473 | 474 | # some additional application parameters: 475 | # - type (generic, protected) used to distinguish the common access application 476 | # and application for which authentication to perform some operation cannot be 477 | # obtained with the common procedures (ex. object creation protected by secure messaging). 478 | # Used by PKCS#11 module configurated to expose restricted number of slots. 479 | # (for ex. configurated to expose only User PIN slot, User and Sign PINs slots, ...) 480 | application E828BD080FD25047656E65726963 { 481 | type = generic; 482 | model = "ECC Generic PKI"; 483 | } 484 | 485 | application E828BD080FD2500000040301 { 486 | type = generic; 487 | model = "Adèle Générique"; 488 | } 489 | 490 | application E828BD080FD2504543432D654944 { 491 | type = protected; 492 | model = "ECC eID"; 493 | } 494 | 495 | application E828BD080FD2500000040201 { 496 | type = protected; 497 | model = "Adèle Admin-2"; 498 | } 499 | } 500 | } 501 | 502 | # Parameters for the OpenSC PKCS11 module 503 | app "opensc-pkcs11" { 504 | pkcs11 { 505 | # Should the module support hotplug of readers as per PKCS#11 v2.20? 506 | # This affects slot changes and PC/SC PnP, as v2.11 applications 507 | # are not allowed to change the length of the slot list. 508 | # Default: true 509 | # plug_and_play = false; 510 | 511 | # Maximum Number of virtual slots. 512 | # If there are more slots than defined here, 513 | # the remaining slots will be hidden from PKCS#11. 514 | # Default: 16 515 | # max_virtual_slots = 32; 516 | 517 | # Maximum number of slots per smart card. 518 | # If the card has fewer keys than defined here, 519 | # the remaining number of slots will be empty. 520 | # Default: 4 521 | # slots_per_card = 2; 522 | 523 | # (max_virtual_slots/slots_per_card) limits the number of readers 524 | # that can be used on the system. Default is then 16/4=4 readers. 525 | 526 | # Normally, the pkcs11 module will create 527 | # the full number of slots defined above by 528 | # num_slots. If there are fewer pins/keys on 529 | # the card, the remaining keys will be empty 530 | # (and you will be able to create new objects 531 | # within them). 532 | # Default: true 533 | # hide_empty_tokens = false; 534 | 535 | # By default, the OpenSC PKCS#11 module will not lock your card 536 | # once you authenticate to the card via C_Login. 537 | # 538 | # Thus the other users or other applications is not prevented 539 | # from connecting to the card and perform crypto operations 540 | # (which may be possible because you have already authenticated 541 | # with the card). This setting is not very secure. 542 | # 543 | # Also, if your card is not locked, you can enconter problems 544 | # due to limitation of the OpenSC framework, that still is not 545 | # thoroughly tested in the multi threads environment. 546 | # 547 | # Your settings will be more secure if you choose to lock your 548 | # card. Nevertheless this behavior is a known violation of PKCS#11 549 | # specification. Now once one application has started using your 550 | # card with C_Login, no other application can use it, until 551 | # the first is done and calls C_Logout or C_Finalize. In the case 552 | # of many PKCS#11 application this does not happen until you exit 553 | # the application. 554 | # Thus it is impossible to use several smart card aware applications 555 | # at the same time, e.g. you cannot run both Firefox and Thunderbird at 556 | # the same time, if both are configured to use your smart card. 557 | # 558 | # Default: false 559 | # lock_login = true; 560 | 561 | # User PIN unblock style 562 | # none: PIN unblock is not possible with PKCS#11 API; 563 | # set_pin_in_unlogged_session: C_SetPIN() in unlogged session: 564 | # PUK is passed as the 'OldPin' argument of the C_SetPIN() call. 565 | # set_pin_in_specific_context: C_SetPIN() in the CKU_SPECIFIC_CONTEXT logged session: 566 | # PUK is passed as the 'OldPin' argument of the C_SetPIN() call. 567 | # init_pin_in_so_session: C_InitPIN() in CKU_SO logged session: 568 | # User PIN 'UNBLOCK' is protected by SOPIN. (PUK == SOPIN). 569 | # # Actually this style works only for the PKCS15 contents without SOPIN. 570 | # # For those with SOPIN, this mode will be usefull for the cards without 571 | # # modes 00 and 01 of ISO command 'RESET RETRY COUNTER'. --vt 572 | # 573 | # Default: none 574 | # user_pin_unblock_style = set_pin_in_unlogged_session; 575 | 576 | # Create slot for unblocking PIN with PUK 577 | # This way PKCS#11 API can be used to login with PUK and 578 | # change a PIN. 579 | # Warning: causes problems with some applications like 580 | # firefox and thunderbird. Thus turned off by default 581 | # 582 | # Default: false 583 | # create_puk_slot = true; 584 | 585 | # Report as 'zero' the CKA_ID attribute of CA certificate 586 | # For the unknown reason the middleware of the manufacturer of gemalto (axalto, gemplus) 587 | # card reports as '0' the CKA_ID of CA cartificates. 588 | # Maybe someone else will need it. (Would be nice to know who and what for -- VTA) 589 | # 590 | # Default: false 591 | # zero_ckaid_for_ca_certs = true; 592 | 593 | # List of readers to ignore 594 | # If any of the strings listed below is matched (case sensitive) in a reader name, 595 | # the reader is ignored by the PKCS#11 module. 596 | # 597 | # Default: empty 598 | # ignored_readers = "CardMan 1021", "SPR 532"; 599 | 600 | # Symbolic names of PINs for which slots are created 601 | # Card can contain more then one PINs or more then one on-card application with 602 | # its own PINs. Normally, to access all of them with the PKCS#11 API a slot has to be 603 | # created for all of them. Many slots could be ennoying for some of widely used application, 604 | # like FireFox. This configuration parameter allows to select the PINs or on-card application 605 | # for which PKCS#11 slot will be created. 606 | # Actually recognised following symbolic names: 607 | # 'user', 'sign', 'application', all 608 | # Only PINs initialised, non-SoPIN, non-unblocking are associated with symbolic name. 609 | # 'user' is identified as first global or first local PIN. 610 | # 'sign' is identified as second PIN: first local, second global or second local. 611 | # 'application' slot created for each on-card application, 612 | # even if they use a common global PIN. 613 | # 'all' slot created for all non-sopin, non-unblocking PINs, 614 | # optionally for PUK (see option 'create_puk_slot') 615 | # 616 | # Default: all 617 | # create_slots_for_pins = "user,sign"; 618 | # create_slots_for_pins = application; 619 | # create_slots_for_pins = "application,sign"; 620 | # 621 | # For the module to simulate the opensc-onepin module behavior the following option 622 | # must be set: 623 | # create_slots_for_pins = "user" 624 | } 625 | } 626 | 627 | app "onepin-opensc-pkcs11" { 628 | pkcs11 { 629 | slots_per_card = 1; 630 | } 631 | } 632 | 633 | # Used by OpenSC.tokend on Mac OS X only. 634 | app tokend { 635 | # The file to which debug log will be written 636 | # Default: /tmp/opensc-tokend.log 637 | # 638 | # debug_file = /Library/Logs/OpenSC.tokend.log 639 | 640 | framework tokend { 641 | # Score for OpenSC.tokend 642 | # The tokend with the highest score shall be used. 643 | # Default: 300 644 | # 645 | # score = 10; 646 | } 647 | } 648 | 649 | # XXX: remove cardmod pseudodriver 650 | app cardmod { 651 | # cardmod app name use special pcsc reader subset 652 | # fix options for this reader driver here. 653 | 654 | reader_driver cardmod { 655 | # Enable pinpad if detected (PC/SC v2.0.2 Part 10) 656 | # Default: true 657 | # enable_pinpad = false; 658 | } 659 | } 660 | -------------------------------------------------------------------------------- /piv-auth.c: -------------------------------------------------------------------------------- 1 | #ifdef WIN32 2 | #undef UNICODE 3 | #endif 4 | 5 | #include 6 | #include 7 | 8 | #ifdef __APPLE__ 9 | #include 10 | #include 11 | #else 12 | #include 13 | #endif 14 | 15 | #ifdef WIN32 16 | static char *pcsc_stringify_error(LONG rv) 17 | { 18 | static char out[20]; 19 | sprintf_s(out, sizeof(out), "0x%08X", rv); 20 | 21 | return out; 22 | } 23 | #endif 24 | 25 | #define CHECK(f, rv) \ 26 | if (SCARD_S_SUCCESS != rv) \ 27 | { \ 28 | printf(f ": %s\n", pcsc_stringify_error(rv)); \ 29 | return -1; \ 30 | } 31 | 32 | int main(void) 33 | { 34 | LONG rv; 35 | 36 | SCARDCONTEXT hContext; 37 | LPTSTR mszReaders; 38 | SCARDHANDLE hCard; 39 | DWORD dwReaders, dwActiveProtocol, dwRecvLength; 40 | 41 | SCARD_IO_REQUEST pioSendPci; 42 | BYTE pbRecvBuffer[258]; 43 | BYTE cmd1[] = { 0x00, 0xA4, 0x04, 0x00, 0x0A, 0xA0, 44 | 0x00, 0x00, 0x00, 0x62, 0x03, 0x01, 0x0C, 0x06, 0x01 }; 45 | BYTE cmd2[] = { 0x00, 0x00, 0x00, 0x00 }; 46 | 47 | unsigned int i; 48 | 49 | rv = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &hContext); 50 | CHECK("SCardEstablishContext", rv) 51 | 52 | #ifdef SCARD_AUTOALLOCATE 53 | dwReaders = SCARD_AUTOALLOCATE; 54 | 55 | rv = SCardListReaders(hContext, NULL, (LPTSTR)&mszReaders, &dwReaders); 56 | CHECK("SCardListReaders", rv) 57 | #else 58 | rv = SCardListReaders(hContext, NULL, NULL, &dwReaders); 59 | CHECK("SCardListReaders", rv) 60 | 61 | mszReaders = calloc(dwReaders, sizeof(char)); 62 | rv = SCardListReaders(hContext, NULL, mszReaders, &dwReaders); 63 | CHECK("SCardListReaders", rv) 64 | #endif 65 | printf("reader name: %s\n", mszReaders); 66 | 67 | rv = SCardConnect(hContext, mszReaders, SCARD_SHARE_SHARED, 68 | SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &hCard, &dwActiveProtocol); 69 | CHECK("SCardConnect", rv) 70 | 71 | switch(dwActiveProtocol) 72 | { 73 | case SCARD_PROTOCOL_T0: 74 | pioSendPci = *SCARD_PCI_T0; 75 | break; 76 | 77 | case SCARD_PROTOCOL_T1: 78 | pioSendPci = *SCARD_PCI_T1; 79 | break; 80 | } 81 | dwRecvLength = sizeof(pbRecvBuffer); 82 | rv = SCardTransmit(hCard, &pioSendPci, cmd1, sizeof(cmd1), 83 | NULL, pbRecvBuffer, &dwRecvLength); 84 | CHECK("SCardTransmit", rv) 85 | 86 | printf("response: "); 87 | for(i=0; i /dev/null 34 | rm $CERT_FILE 2> /dev/null 35 | rm $CHALHASH_FILE 2> /dev/null 36 | rm $PUB_KEY_FILE 2> /dev/null 37 | rm $SIG_FILE 2> /dev/null 38 | rm $SIG2_FILE 2> /dev/null 39 | rm $ATR_FILE 2> /dev/null 40 | rm $SERIAL_FILE 2> /dev/null 41 | rm $TMP_FILE 2> /dev/null 42 | } 43 | 44 | cache_cert() { 45 | cache_file="$CACHE_DIR/`cat $SERIAL_FILE | openssl sha1 | cut -d ' ' -f 2`" 46 | mkdir -p "$CACHE_DIR" 47 | cp "$CERT_FILE" "$cache_file" 48 | } 49 | 50 | cache_lookup() { 51 | cache_file="$CACHE_DIR/`cat $SERIAL_FILE | openssl sha1 | cut -d ' ' -f 2`" 52 | if [ -f "$cache_file" ] 53 | then 54 | #echo Cache hit 55 | cp "$cache_file" "$CERT_FILE" 56 | else return 1 57 | fi 58 | } 59 | 60 | uncache_cert() { 61 | cache_file="$CACHE_DIR/`cat $SERIAL_FILE | openssl sha1 | cut -d ' ' -f 2`" 62 | rm -f "$cache_file" 63 | } 64 | 65 | 66 | die() { 67 | grep -v -e "Sending" -e "Receiv" "$SERIAL_FILE" | tail -n 1 2> "$STDERR" 68 | #log the failed AccessAttempt 69 | #echo logging 70 | logToDB 0 71 | [ "0$DEBUG" -lt 1 ] && cleanup 72 | 73 | #AntiBruteforce-Timelock 74 | #If 3 times within 30 seconds a AuthenticationFailure is recognized, the Authentication will be locked for 30 seconds. 75 | #For performance reasons the FailureTimestamps.txt is used instead of logDB.sqlite 76 | #Save NewestFailureTimestamp at the End of the file 77 | echo $(date +%s) >> FailureTimestamps.txt 78 | if [ $(wc -l FailureTimestamps.txt 81 | if [ $(($(tail -1 FailureTimestamps.txt)-$(head -1 FailureTimestamps.txt))) -lt 30 ] 82 | then sleep 30s 83 | fi 84 | fi 85 | echo died 86 | exit 1 87 | } 88 | 89 | #logToDB logs Information about Identity contained in the certificte into the database. So one is able to reconstruct who got when Access. 90 | logToDB() { 91 | #walk through multilined subject (line by line) and extract Identity-values 92 | LINE_NR="0" 93 | if [ -e $CERT_FILE ]; then 94 | while true 95 | do 96 | LINE_NR=$(($LINE_NR+1)) 97 | #'subject=' means one has reached the first line (the loop is starting at the most bottom line) 98 | if [ $(printf $(openssl x509 -in $CERT_FILE -noout -subject -nameopt sep_multiline,lname | tail -$LINE_NR | head -1)) = 'subject=' 1> /dev/null ]; then break 99 | else 100 | if openssl x509 -in $CERT_FILE -noout -subject -nameopt sep_multiline,lname | tail -$LINE_NR | head -1 | grep "emailAddress=" 1> /dev/null; then 101 | EMAIL=$(openssl x509 -in $CERT_FILE -noout -subject -nameopt sep_multiline,lname | tail -$LINE_NR | head -1 | sed 's/\.*emailAddress=//') 102 | elif openssl x509 -in $CERT_FILE -noout -subject -nameopt sep_multiline,lname | tail -$LINE_NR | head -1 | grep "commonName=" 1> /dev/null; then 103 | COMMONNAME=$(openssl x509 -in $CERT_FILE -noout -subject -nameopt sep_multiline,lname | tail -$LINE_NR | head -1 | sed 's/\.*commonName=//') 1> /dev/null 104 | elif openssl x509 -in $CERT_FILE -noout -subject -nameopt sep_multiline,lname | tail -$LINE_NR | head -1 | grep "organizationalUnitName=" 1> /dev/null; then 105 | ORGANIZATIONALUNITNAME=$(openssl x509 -in $CERT_FILE -noout -subject -nameopt sep_multiline,lname | tail -$LINE_NR | head -1 | sed 's/\.*organizationalUnitName=//') 106 | elif openssl x509 -in $CERT_FILE -noout -subject -nameopt sep_multiline,lname | tail -$LINE_NR | head -1 | grep "organizationName=" 1> /dev/null; then 107 | ORGANIZATIONNAME=$(openssl x509 -in $CERT_FILE -noout -subject -nameopt sep_multiline,lname | tail -$LINE_NR | head -1 | sed 's/\.*organizationName=//') 108 | elif openssl x509 -in $CERT_FILE -noout -subject -nameopt sep_multiline,lname | tail -$LINE_NR | head -1 | grep "localityName=" 1> /dev/null; then 109 | LOCALITYNAME=$(openssl x509 -in $CERT_FILE -noout -subject -nameopt sep_multiline,lname | tail -$LINE_NR | head -1 | sed 's/\.*localityName=//') 110 | elif openssl x509 -in $CERT_FILE -noout -subject -nameopt sep_multiline,lname | tail -$LINE_NR | head -1 | grep "stateOrProvinceName=" 1> /dev/null; then 111 | STATEORPROVINCENAME=$(openssl x509 -in $CERT_FILE -noout -subject -nameopt sep_multiline,lname | tail -$LINE_NR | head -1 | sed 's/\.*stateOrProvinceName=//') 112 | elif openssl x509 -in $CERT_FILE -noout -subject -nameopt sep_multiline,lname | tail -$LINE_NR | head -1 | grep "countryName=" 1> /dev/null; then 113 | COUNTRYNAME=$(openssl x509 -in $CERT_FILE -noout -subject -nameopt sep_multiline,lname | tail -$LINE_NR | head -1 | sed 's/\.*countryName=//') 114 | fi 115 | fi 116 | done 117 | fi 118 | 119 | #look wether Identity tried to get Access somewhere in the past 120 | IDofExistingIdentity=$(sqlite3 ./logDB.sqlite "Select ID from Identity WHERE email='$EMAIL' AND CommonName='$COMMONNAME' AND OrganizationalUnitName='$ORGANIZATIONALUNITNAME' AND OrganizationName='$ORGANIZATIONNAME' AND LocalityName='$LOCALITYNAME' AND StateOrProvinceName ='$STATEORPROVINCENAME' AND CountryName='$COUNTRYNAME'") 121 | #if Identity hasn´t tried to get Access in the past, insert it and look up the ID under which it was inserted 122 | if (test -z $IDofExistingIdentity) 123 | then sqlite3 ./logDB.sqlite "Insert INTO Identity (email, CommonName, OrganizationalUnitName, OrganizationName, LocalityName, StateOrProvinceName, CountryName) VALUES ('$EMAIL', '$COMMONNAME', '$ORGANIZATIONALUNITNAME', '$ORGANIZATIONNAME', '$LOCALITYNAME', '$STATEORPROVINCENAME', '$COUNTRYNAME');" 124 | IDofExistingIdentity=$(sqlite3 ./logDB.sqlite "Select ID from Identity WHERE email='$EMAIL' AND CommonName='$COMMONNAME' AND OrganizationalUnitName='$ORGANIZATIONALUNITNAME' AND OrganizationName='$ORGANIZATIONNAME' AND LocalityName='$LOCALITYNAME' AND StateOrProvinceName ='$STATEORPROVINCENAME' AND CountryName='$COUNTRYNAME'") 125 | fi 126 | #Actual Logging into database 127 | sqlite3 ./logDB.sqlite "Insert INTO AccessAttempt (Identity_ID, timestamp, success) VALUES ('$IDofExistingIdentity', CURRENT_TIMESTAMP, '$1');"; 128 | } 129 | 130 | 131 | 132 | cleanup 133 | 134 | # Get ATR 135 | #opensc-tool -w -a 2> "$STDERR" > "$ATR_FILE" 136 | 137 | # Calculate a challenge 138 | dd if="$RAND_FILE" of="$CHAL_FILE" bs=32 count=1 2> "$STDERR" || die 139 | 140 | # Calculate the hash of the challenge 141 | openssl sha -sha256 -binary < "$CHAL_FILE" > "$CHALHASH_FILE" || die 142 | 143 | # Get Serial 144 | opensc-tool -w --send-apdu FFCA000000 --send-apdu 00:a4:04:00:09:a0:00:00:03:08:00:00:10:00 --send-apdu 00:cb:3f:ff:05:5C:03:5f:C1:02 2> $STDERR > "$SERIAL_FILE" || die 145 | #opensc-tool --serial --send-apdu FFCA000000 2> $STDERR > "$SERIAL_FILE" || die 146 | 147 | # See if the card is in the cache, and if so load it up. 148 | cache_lookup || { 149 | # Extract the certificate 150 | pkcs15-tool $PKCS15_CRYPT_FLAGS -L --read-certificate "$KEY_ID" -o "$CERT_FILE" > "$STDERR" 2> "$STDERR" || die 151 | } 152 | 153 | # Calculate the response for the challenge 154 | if ( openssl x509 -in cert.pem -text | grep -q -s "Public Key Algorithm: id-ecPublicKey" ) ; 155 | then 156 | # ECDSA signatures need to be converted to DER format. 157 | pkcs15-crypt $PKCS15_CRYPT_FLAGS -s -k $KEY_ID --sha-256 -i $CHALHASH_FILE -o $SIG2_FILE 2> $STDERR || die 158 | ./ecdsa-pkcs11-to-asn1 < $SIG2_FILE > $SIG_FILE || die 159 | else 160 | pkcs15-crypt $PKCS15_CRYPT_FLAGS -s -k $KEY_ID --sha-256 --pkcs1 -i $CHALHASH_FILE -o $SIG_FILE 2> $STDERR || die 161 | fi 162 | 163 | # Verify the certificate 164 | cat $CERT_FILE | openssl verify -crl_check -CAfile ca.crt -verbose -purpose sslclient > $TMP_FILE || { 165 | uncache_cert 166 | die 167 | } 168 | 169 | # The openssl verify command is very lenient when it comes to 170 | # self-signed certificates. This is an obvious security hole in this 171 | # use case. The following check makes sure that if there is anything 172 | # except perfect verification that we fail. 173 | [ $STRICT_CHECK = 1 ] && [ "`cat $TMP_FILE`" '!=' "stdin: OK" ] && die 174 | 175 | # Extract the public key 176 | openssl x509 -pubkey -noout -in $CERT_FILE > $PUB_KEY_FILE || die 177 | 178 | # Verify the response 179 | openssl dgst -sha256 -verify $PUB_KEY_FILE -signature $SIG_FILE $CHAL_FILE 2>&1 > $STDERR || die 180 | 181 | # Print out the subject name 182 | openssl x509 -in $CERT_FILE -nameopt RFC2253 -noout -subject | sed 's:^[^ ]* ::' | tee $SUBJECT_DN_FILE || die 183 | 184 | #log the successfull AccessAttempt 185 | logToDB 1 186 | 187 | # Authentification was successful, so cache the cert so we don't have to read it again. 188 | cache_cert 189 | 190 | cleanup 191 | echo successfull 192 | exit 0 --------------------------------------------------------------------------------