diff --git a/npa/src/opensc b/npa/src/opensc index 34a69ae..db60f8d 160000 --- a/npa/src/opensc +++ b/npa/src/opensc @@ -1 +1 @@ -Subproject commit 34a69aeac81e341aefe9f19118dd7a2e3b29ac3b +Subproject commit db60f8da59ef05fa987decfc5a6b5abb9bc93f42 diff --git a/virtualsmartcard/src/vpicc/vicc.in b/virtualsmartcard/src/vpicc/vicc.in index 68998db..f360960 100644 --- a/virtualsmartcard/src/vpicc/vicc.in +++ b/virtualsmartcard/src/vpicc/vicc.in @@ -2,19 +2,19 @@ # # Copyright (C) 2010-2012 Frank Morgner # Copyright (C) 2010 Dominik Oepen -# +# # This file is part of virtualsmartcard. -# +# # virtualsmartcard 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. -# +# # virtualsmartcard 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 # virtualsmartcard. If not, see . # @@ -152,10 +152,9 @@ if args.reversed: else: hostname = args.hostname -vicc = VirtualICC(args.file, args.datasetfile, args.type, - hostname, args.port, readernum=args.reader, - ef_cardaccess=ef_cardaccess_data, ef_cardsecurity=ef_cardsecurity_data, - ca_key=ca_key_data, cvca=cvca, disable_checks=args.disable_ta_checks, - esign_ca_cert=esign_ca_cert, esign_cert=esign_cert, - logginglevel=logginglevel) +vicc = VirtualICC(args.datasetfile, args.type, hostname, args.port, + readernum=args.reader, ef_cardaccess=ef_cardaccess_data, + ef_cardsecurity=ef_cardsecurity_data, ca_key=ca_key_data, cvca=cvca, + disable_checks=args.disable_ta_checks, esign_ca_cert=esign_ca_cert, + esign_cert=esign_cert, logginglevel=logginglevel) vicc.run() diff --git a/virtualsmartcard/src/vpicc/virtualsmartcard/CardGenerator.py b/virtualsmartcard/src/vpicc/virtualsmartcard/CardGenerator.py index cc1a7e5..93c5ef3 100644 --- a/virtualsmartcard/src/vpicc/virtualsmartcard/CardGenerator.py +++ b/virtualsmartcard/src/vpicc/virtualsmartcard/CardGenerator.py @@ -17,7 +17,6 @@ # virtualsmartcard. If not, see . # -import anydbm import binascii import getpass import logging @@ -32,7 +31,6 @@ from virtualsmartcard.TLVutils import pack from virtualsmartcard.utils import inttostring from virtualsmartcard.SmartcardFilesystem import MF, DF, TransparentStructureEF from virtualsmartcard.ConstantDefinitions import FDB, ALGO_MAPPING -from virtualsmartcard.CryptoUtils import protect_string, read_protected_string from virtualsmartcard.SmartcardSAM import SAM # pgp directory @@ -693,50 +691,6 @@ class CardGenerator(object): if sam is not None: self.sam = sam - def loadCard(self, filename): - """Load a card from disk""" - if self.password is None: - self.password = getpass.getpass("Please enter your password:") - - db = anydbm.open(filename, 'r') - try: - serializedMF = read_protected_string(db["mf"], self.password) - serializedSAM = read_protected_string(db["sam"], self.password) - self.type = db["type"] - finally: - db.close() - - self.sam = loads(serializedSAM) - self.mf = loads(serializedMF) - - def saveCard(self, filename): - """Save the currently running card to disk""" - if self.password is None: - passwd1 = getpass.getpass("Please enter your password:") - passwd2 = getpass.getpass("Please retype your password:") - if (passwd1 != passwd2): - raise ValueError("Passwords did not match. Will now exit") - else: - self.password = passwd1 - - if self.mf is None or self.sam is None: - raise ValueError("Card Generator wasn't set up properly" + - "(missing MF or SAM).") - - mf_string = dumps(self.mf) - sam_string = dumps(self.sam) - protectedMF = protect_string(mf_string, self.password) - protectedSAM = protect_string(sam_string, self.password) - - db = anydbm.open(filename, 'c') - try: - db["mf"] = protectedMF - db["sam"] = protectedSAM - db["type"] = self.type - db["version"] = "0.1" - finally: - db.close() - def readDatagroups(self, datasetfile): """Read Datagroups from file""" with open(datasetfile, 'r') as f: @@ -759,15 +713,7 @@ if __name__ == "__main__": default='iso7816', choices=['iso7816', 'cryptoflex', 'ePass', 'nPA'], help="Type of Smartcard [default: %default]") - parser.add_option("-f", "--file", action="store", type="string", - dest="filename", default=None, - help="Where to save the generated card") (options, args) = parser.parse_args() - if options.filename is None: - logging.error("You have to provide a filename using the -f option") - sys.exit() - generator = CardGenerator(options.type) generator.generateCard() - generator.saveCard(options.filename) diff --git a/virtualsmartcard/src/vpicc/virtualsmartcard/CryptoUtils.py b/virtualsmartcard/src/vpicc/virtualsmartcard/CryptoUtils.py index d782ee6..eb9c6b9 100644 --- a/virtualsmartcard/src/vpicc/virtualsmartcard/CryptoUtils.py +++ b/virtualsmartcard/src/vpicc/virtualsmartcard/CryptoUtils.py @@ -21,7 +21,6 @@ import logging import re from struct import pack from base64 import b64encode -from hashlib import pbkdf2_hmac from random import randint from virtualsmartcard.utils import inttostring @@ -217,72 +216,6 @@ def operation_on_string(string1, string2, op): return "".join(result) -def protect_string(string, password, cipherspec=None): - """ - Encrypt and authenticate a string - """ - - # Derive a key and a salt from password - pbk = crypt(password) - regex = re.compile('\$p5k2\$(\d*)\$([\w./]*)\$([\w./]*)') - match = regex.match(pbk) - if match is not None: - iterations = match.group(1) - salt = match.group(2) - key = match.group(3) - else: - raise ValueError - - # Encrypt the string, authenticate and format it - if cipherspec is None: - cipherspec = "AES-CBC" - else: - pass # TODO: Sanity check for cipher - blocklength = get_cipher_blocklen(cipherspec) - paddedString = append_padding(blocklength, string) - cryptedString = cipher(True, cipherspec, key, paddedString) - hmac = crypto_checksum("HMAC", key, cryptedString) - protectedString = "$p5k2$$" + salt + "$" + cryptedString + hmac - - return protectedString - - -def read_protected_string(string, password, cipherspec=None): - """ - Decrypt a protected string and verify the authentication data - """ - hmac_length = 32 # FIXME: Ugly - - # Check if the string has the structure, that is generated by - # protect_string - regex = re.compile('\$p5k2\$\$([\w./]*)\$') - match = regex.match(string) - if match is not None: - salt = match.group(1) - crypted = string[match.end():len(string) - hmac_length] - hmac = string[len(string) - hmac_length:] - else: - raise ValueError("Wrong string format") - - # Derive key - pbk = crypt(password, salt) - match = regex.match(pbk) - key = pbk[match.end():] - - # Verify HMAC - checksum = crypto_checksum("HMAC", key, crypted) - if checksum != hmac: - logging.error("Found HMAC %s expected %s" % (str(hmac), str(checksum))) - raise ValueError("Failed to authenticate data. Wrong password?") - - # Decrypt data - if cipherspec is None: - cipherspec = "AES-CBC" - decrypted = cipher(False, cipherspec, key, crypted) - - return strip_padding(get_cipher_blocklen(cipherspec), decrypted) - - # ******************************************************************* # * Cyberflex specific methods * # ******************************************************************* @@ -297,62 +230,6 @@ def calculate_MAC(session_key, message, iv=CYBERFLEX_IV): return crypted[len(padded) - cipher.block_size:] - -def crypt(word, salt=None, iterations=None): - """PBKDF2-based unix crypt(3) replacement. - - The number of iterations specified in the salt overrides the 'iterations' - parameter. - - The effective hash length is 192 bits. - """ - - # Generate a (pseudo-)random salt if the user hasn't provided one. - if salt is None: - salt = _makesalt() - - # salt must be a string or the us-ascii subset of unicode - if isinstance(salt, unicode): - salt = salt.encode("us-ascii") - if not isinstance(salt, str): - raise TypeError("salt must be a string") - - # word must be a string or unicode (in the latter case, we convert to - # UTF-8) - if isinstance(word, unicode): - word = word.encode("UTF-8") - if not isinstance(word, str): - raise TypeError("word must be a string or unicode") - - # Try to extract the real salt and iteration count from the salt - if salt.startswith("$p5k2$"): - (iterations, salt, dummy) = salt.split("$")[2:5] - if iterations == "": - iterations = 400 - else: - converted = int(iterations, 16) - if iterations != "%x" % converted: # lowercase hex, minimum digits - raise ValueError("Invalid salt") - iterations = converted - if not (iterations >= 1): - raise ValueError("Invalid salt") - - # Make sure the salt matches the allowed character set - allowed = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345678"\ - "9./" - for ch in salt: - if ch not in allowed: - raise ValueError("Illegal character %r in salt" % (ch,)) - - if iterations is None or iterations == 400: - iterations = 400 - salt = "$p5k2$$" + salt - else: - salt = "$p5k2$%x$%s" % (iterations, salt) - rawhash = pbkdf2_hmac('sha1', salt, word, iterations, 24) - return salt + "$" + b64encode(rawhash, "./") - - def _makesalt(): """Return a 48-bit pseudorandom salt for crypt(). diff --git a/virtualsmartcard/src/vpicc/virtualsmartcard/VirtualSmartcard.py b/virtualsmartcard/src/vpicc/virtualsmartcard/VirtualSmartcard.py index 22399a9..dbbf979 100644 --- a/virtualsmartcard/src/vpicc/virtualsmartcard/VirtualSmartcard.py +++ b/virtualsmartcard/src/vpicc/virtualsmartcard/VirtualSmartcard.py @@ -387,7 +387,7 @@ class VirtualICC(object): the vpcd, which forwards it to the application. """ - def __init__(self, filename, datasetfile, card_type, host, port, + def __init__(self, datasetfile, card_type, host, port, readernum=None, ef_cardsecurity=None, ef_cardaccess=None, ca_key=None, cvca=None, disable_checks=False, esign_key=None, esign_ca_cert=None, esign_cert=None, @@ -398,18 +398,8 @@ class VirtualICC(object): format="%(asctime)s [%(levelname)s] %(message)s", datefmt="%d.%m.%Y %H:%M:%S") - self.filename = None self.cardGenerator = CardGenerator(card_type) - # If a filename is specified, try to load the card from disk - if filename is not None: - self.filename = filename - if exists(filename): - self.cardGenerator.loadCard(self.filename) - else: - logging.info("Creating new card which will be saved in %s.", - self.filename) - # If a dataset file is specified, read the card's data groups from disk if datasetfile is not None: if exists(datasetfile): @@ -572,6 +562,3 @@ class VirtualICC(object): self.sock.close() if self.server_sock: self.server_sock.close() - if self.filename is not None: - self.cardGenerator.setCard(self.os.mf, self.os.SAM) - self.cardGenerator.saveCard(self.filename) diff --git a/virtualsmartcard/src/vpicc/virtualsmartcard/tests/CardGenerator_test.py b/virtualsmartcard/src/vpicc/virtualsmartcard/tests/CardGenerator_test.py index e8eb97d..e4225df 100644 --- a/virtualsmartcard/src/vpicc/virtualsmartcard/tests/CardGenerator_test.py +++ b/virtualsmartcard/src/vpicc/virtualsmartcard/tests/CardGenerator_test.py @@ -17,7 +17,6 @@ # virtualsmartcard. If not, see . # -import anydbm import os import tempfile import unittest @@ -30,32 +29,13 @@ class ISO7816GeneratorTest(unittest.TestCase): card_type = 'iso7816' def setUp(self): - self.filename = tempfile.mktemp() self.card_generator = CardGenerator(self.card_type) - self.card_generator.password = "TestPassword" def test_card_creation(self): self.card_generator.generateCard() - self.card_generator.saveCard(self.filename) mf, sam = self.card_generator.getCard() self.assertIsNotNone(mf) self.assertIsNotNone(sam) - os.unlink(self.filename) - - def test_load_card_from_file(self): - self.card_generator.generateCard() - self.card_generator.saveCard(self.filename) - local_generator = CardGenerator(self.card_type) - local_generator.password = self.card_generator.password - local_generator.loadCard(self.filename) - mf, sam = local_generator.getCard() - self.assertIsNotNone(mf) - self.assertIsNotNone(sam) - os.unlink(self.filename) - - def test_load_nonexistent_file(self): - with self.assertRaises(anydbm.error): - self.card_generator.loadCard(self.filename) def test_get_and_set_card(self): self.card_generator.generateCard() @@ -69,9 +49,7 @@ class TestNPACardGenerator(ISO7816GeneratorTest): card_type = 'nPA' def setUp(self): - self.filename = tempfile.mktemp() self.card_generator = CardGenerator(self.card_type) - self.card_generator.password = "TestPassword" self.test_readDatagroups_file = "/../../../../npa-example-data/"\ "Example_Dataset_Mueller_Gertrud.txt" diff --git a/virtualsmartcard/src/vpicc/virtualsmartcard/tests/CryptoUtils_test.py b/virtualsmartcard/src/vpicc/virtualsmartcard/tests/CryptoUtils_test.py index 66d3509..490321e 100644 --- a/virtualsmartcard/src/vpicc/virtualsmartcard/tests/CryptoUtils_test.py +++ b/virtualsmartcard/src/vpicc/virtualsmartcard/tests/CryptoUtils_test.py @@ -26,46 +26,11 @@ class TestCryptoUtils(unittest.TestCase): def setUp(self): self.teststring = "DEADBEEFistatsyksdvhwohfwoehcowc8hw8rogfq8whv75tsg"\ "ohsav8wress" - self.testpass = "SomeRandomPassphrase" - # The following string was generated using the proteced string method - # and is used as a regression test. - # The data generated by protect_string should actually consist of - # printable characters only but that would break backwards - # compatibility with the (buggy) legacy implementation - self.protectedTestString = "2470356b32242478424f50746f6d712448b8f6285"\ - "ac8462fffc6aef921f2ad84855219c5aaafb39c4c"\ - "c9e54d1634c60cfc9347c67fa55967c5b0130469c"\ - "96a44f0b73c53f5ddfc43cd8c1ef68965ebb23330"\ - "39326538373233393735346565383864313536383"\ - "0666637336134316532".decode('hex') - self.salt = "POcwYIHr" - self.cryptedWord = "$p5k2$$POcwYIHr$SPaWqD3NpmLZc6gXbeybnAoCxo7Oc//K" - self.cryptedWordThousandIterations = "$p5k2$3e8$POcwYIHr$f/mEOCulo6v7"\ - "Nq2ooS3480xTet6zdGbI" def test_padding(self): padded = append_padding(16, self.teststring) unpadded = strip_padding(16, padded) self.assertEqual(unpadded, self.teststring) - def test_protect_string(self): - protectedString = protect_string(self.teststring, self.testpass) - unprotectedString = read_protected_string(protectedString, - self.testpass) - self.assertEqual(self.teststring, unprotectedString) - - def test_unprotect_string(self): - unprotectedString = read_protected_string(self.protectedTestString, - self.testpass) - self.assertEqual(unprotectedString, self.teststring) - - def test_crypt(self): - cryptedWord = crypt(self.teststring, self.salt) - self.assertEqual(cryptedWord, self.cryptedWord) - - def test_crypt_non_default_iterations(self): - cryptedWord = crypt(self.teststring, self.salt, 1000) - self.assertEqual(cryptedWord, self.cryptedWordThousandIterations) - if __name__ == "__main__": unittest.main()