Source code for linotp.lib.security.default

# -*- coding: utf-8 -*-
#
#    LinOTP - the open source solution for two factor authentication
#    Copyright (C) 2010 - 2014 LSE Leading Security Experts GmbH
#
#    This file is part of LinOTP server.
#
#    This program is free software: you can redistribute it and/or
#    modify it under the terms of the GNU Affero General Public
#    License, version 3, as published by the Free Software Foundation.
#
#    This program 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 Affero General Public License for more details.
#
#    You should have received a copy of the
#               GNU Affero General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
#
#    E-mail: linotp@lsexperts.de
#    Contact: www.linotp.org
#    Support: www.lsexperts.de
#
"""default SecurityModules which takes the enc keys from a file"""


import logging
import binascii
import os

from Crypto.Cipher import AES

from linotp.lib.crypt import zerome
from linotp.lib.security import SecurityModule


TOKEN_KEY = 0
CONFIG_KEY = 1
VALUE_KEY = 2
DEFAULT_KEY = 3


log = logging.getLogger(__name__)


[docs]class DefaultSecurityModule(SecurityModule): def __init__(self, config=None): ''' initialsation of the security module :param config: contains the configuration definition :type config: - dict - :return - ''' self.name = "Default" self.config = config self.crypted = False self.is_ready = True self._id = binascii.hexlify(os.urandom(3)) if config.has_key('crypted'): crypt = config.get('crypted').lower() if crypt == 'true': self.crypted = True self.is_ready = False if config.has_key('file') == False: log.error("[getSecret] no secret file defined. A parameter linotpSecretFile is missing in your linotp.ini.") raise Exception("no secret file defined: linotpSecretFile!") self.secFile = config.get('file') self.secrets = {} return
[docs] def isReady(self): ''' provides the status, if the security module is fully initializes this is required especially for the runtime confi like set password ++ :return: status, if the module is fully operational :rtype: boolean ''' return self.is_ready
[docs] def getSecret(self, id=0): ''' internal function, which acceses the key in the defined slot :param id: slot id of the key array :type id: int :return: key or secret :rtype: binary string ''' log.debug('getSecret()') id = int(id) if self.crypted: if self.secrets.has_key(id): return self.secrets.get(id) secret = '' try: f = open(self.secFile) for _i in range (0 , id + 1): secret = f.read(32) f.close() if secret == "" : #secret = setupKeyFile(secFile, id+1) raise Exception ("No secret key defined for index: %s !\n" "Please extend your %s"" !" % (str(id), self.secFile)) except Exception as e: raise Exception ("Exception:" + unicode(e)) if self.crypted: self.secrets[id] = secret return secret;
[docs] def setup_module(self, param): ''' callback, which is called during the runtime to initialze the security module :param params: all parameters, which are provided by the http request :type params: dict :return: - ''' if self.crypted == False: return if param.has_key('password') == False: raise Exception("missing password") ## if we have a crypted file and a password, we take all keys ## from the file and put them in a hash ## ## After this we do not require the password anymore handles = ['pinHandle' , 'passHandle' , 'valueHandle', 'defaultHandle'] for handle in handles: self.getSecret(self.config.get(handle, '0')) self.is_ready = True return ## the real interfaces: random, encrypt, decrypt '''
[docs] def random(self, len=32): ''' security module methods: random :param len: length of the random byte array :type len: int :return: random bytes :rtype: byte string ''' log.debug('random()') return os.urandom(len)
[docs] def encrypt(self, data, iv, id=0): ''' security module methods: encrypt :param data: the to be encrypted data :type data:byte string :param iv: initialisation vector (salt) :type iv: random bytes :param id: slot of the key array :type id: int :return: encrypted data :rtype: byte string ''' log.debug('encrypt()') if self.is_ready == False: raise Exception('setup of security module incomplete') key = self.getSecret(id) ## convert input to ascii, so we can securely append bin data input = binascii.b2a_hex(data) input += u"\x01\x02" padding = (16 - len(input) % 16) % 16 input += padding * "\0" aes = AES.new(key, AES.MODE_CBC, iv) # cko: ARGH: Only ECB! #import linotp.lib.yhsm as yhsm #y = yhsm.YubiHSM(0x1111, password="14fda9321ae820aa34e57852a31b10d0") #y.unlock(password="14fda9321ae820aa34e57852a31b10d0") #res = y.encrypt(input) # res = aes.encrypt(input) if self.crypted == False: zerome(key) del key return res
[docs] def decrypt(self, input, iv, id=0): ''' security module methods: decrypt :param data: the to be decrypted data :type data:byte string :param iv: initialisation vector (salt) :type iv: random bytes :param id: slot of the key array :type id: int :return: decrypted data :rtype: byte string ''' log.debug('decrypt()') if self.is_ready == False: raise Exception('setup of security module incomplete') key = self.getSecret(id) aes = AES.new(key, AES.MODE_CBC, iv) # cko #import linotp.lib.yhsm as yhsm #y = yhsm.YubiHSM(0x1111, password="14fda9321ae820aa34e57852a31b10d0") #y.unlock(password="14fda9321ae820aa34e57852a31b10d0") #log.debug("CKO in: %s" % input) #output = binascii.hexlify(y.decrypt(input)) #log.debug("CKO out: %s" % output) # output = aes.decrypt(input) #log.debug("CKO: output2: %s" % output) eof = output.rfind(u"\x01\x02") if eof >= 0: output = output[:eof] ## convert output from ascii, back to bin data data = binascii.a2b_hex(output) if self.crypted == False: zerome(key) del key return data
[docs] def decryptPassword(self, cryptPass): ''' dedicated security module methods: decryptPassword which used one slot id to decryt a string :param cryptPassword: the crypted password - leading iv, seperated by the ':' :param cryptPassword: byte string :return: decrypted data :rtype: byte string ''' return self._decryptValue(cryptPass, CONFIG_KEY)
[docs] def decryptPin(self, cryptPin): ''' dedicated security module methods: decryptPin which used one slot id to decryt a string :param cryptPin: the crypted pin - - leading iv, seperated by the ':' :param cryptPin: byte string :return: decrypted data :rtype: byte string ''' return self._decryptValue(cryptPin, TOKEN_KEY)
[docs] def encryptPassword(self, password): ''' dedicated security module methods: encryptPassword which used one slot id to encrypt a string :param password: the to be encrypted password :param password: byte string :return: encrypted data - leading iv, seperated by the ':' :rtype: byte string ''' return self._encryptValue(password, CONFIG_KEY)
[docs] def encryptPin(self, pin): ''' dedicated security module methods: encryptPin which used one slot id to encrypt a string :param pin: the to be encrypted pin :param pin: byte string :return: encrypted data - leading iv, seperated by the ':' :rtype: byte string ''' return self._encryptValue(pin, TOKEN_KEY)
''' base methods for pin and password ''' def _encryptValue(self, value, keyNum): ''' _encryptValue - base method to encrypt a value - uses one slot id to encrypt a string retrurns as string with leading iv, seperated by ':' :param value: the to be encrypted value :param value: byte string :param id: slot of the key array :type id: int :return: encrypted data with leading iv and sepeartor ':' :rtype: byte string ''' iv = self.random(16) v = self.encrypt(value, iv , keyNum) value = binascii.hexlify(iv) + ':' + binascii.hexlify(v) return value def _decryptValue(self, cryptValue, keyNum): ''' _decryptValue - base method to decrypt a value - used one slot id to encrypt a string with leading iv, seperated by ':' :param cryptValue: the to be encrypted value :param cryptValue: byte string :param id: slot of the key array :type id: int :return: decrypted data :rtype: byte string ''' ## split at ":" pos = cryptValue.find(':') bIV = cryptValue[:pos] bData = cryptValue[pos + 1:len(cryptValue)] iv = binascii.unhexlify(bIV) data = binascii.unhexlify(bData) password = self.decrypt(data, iv, keyNum) return password
[docs]class ErrSecurityModule(DefaultSecurityModule):
[docs] def setup_module(self, params): ret = DefaultSecurityModule.setup_module(self, params) self.is_ready = False return ret #eof###########################################################################