def rand(): """ This endpoint can be used to retrieve random keys from privacyIDEA. In certain cases the client might need random data to initialize tokens on the client side. E.g. the command line client when initializing the yubikey or the WebUI when creating Client API keys for the yubikey. In this case, privacyIDEA can create the random data/keys. :queryparam len: The length of a symmetric key (byte) :queryparam encode: The type of encoding. Can be "hex" or "b64". :return: key material """ length = int(getParam(request.all_data, "len") or 20) encode = getParam(request.all_data, "encode") r = geturandom(length=length) if encode == "b64": res = b64encode_and_unicode(r) else: res = hexlify_and_unicode(r) g.audit_object.log({'success': res}) return send_result(res)
def test_26_conversions(self): self.assertEquals(hexlify_and_unicode(u'Hallo'), u'48616c6c6f') self.assertEquals(hexlify_and_unicode(b'Hallo'), u'48616c6c6f') self.assertEquals(hexlify_and_unicode(b'\x00\x01\x02\xab'), u'000102ab') self.assertEquals(b32encode_and_unicode(u'Hallo'), u'JBQWY3DP') self.assertEquals(b32encode_and_unicode(b'Hallo'), u'JBQWY3DP') self.assertEquals(b32encode_and_unicode(b'\x00\x01\x02\xab'), u'AAAQFKY=') self.assertEquals(b64encode_and_unicode(u'Hallo'), u'SGFsbG8=') self.assertEquals(b64encode_and_unicode(b'Hallo'), u'SGFsbG8=') self.assertEquals(b64encode_and_unicode(b'\x00\x01\x02\xab'), u'AAECqw==') self.assertEquals(urlsafe_b64encode_and_unicode(u'Hallo'), u'SGFsbG8=') self.assertEquals(urlsafe_b64encode_and_unicode(b'Hallo'), u'SGFsbG8=') self.assertEquals(urlsafe_b64encode_and_unicode(b'\x00\x01\x02\xab'), u'AAECqw==') self.assertEquals(urlsafe_b64encode_and_unicode(b'\xfa\xfb\xfc\xfd\xfe\xff'), u'-vv8_f7_')
def get_init_detail(self, params=None, user=None): """ At the end of the initialization we return the certificate and the PKCS12 file, if the private key exists. """ response_detail = TokenClass.get_init_detail(self, params, user) params = params or {} certificate = self.get_tokeninfo("certificate") response_detail["certificate"] = certificate privatekey = self.get_tokeninfo("privatekey") # If there is a private key, we dump a PKCS12 if privatekey: response_detail["pkcs12"] = b64encode_and_unicode(self._create_pkcs12_bin()) return response_detail
def test_27_images(self): png_b64 = u'iVBORw0KGgoAAAANSUhEUgAAASIAAAEiAQAAAAB1xeIbAAABgElEQVR4nO2ZQ' \ u'Y6DMBAEaxbujpQH5CnwdPOglexjJKLeQ2xCsofdC4HA+IDAlERrGKx2Y+LvMX' \ u'z9AwKnnHLKKae2TlkZLZBbrM91pl9V1yGoTpKUwOx0M0UaSZKeqffrOgSVS49' \ u'LqZH1QPkMVtZ1KGq4jG9+4mGp9uXaoP1d/K2q3wcVJEVAsd6RNL5S79e1Z6r0' \ u'/WAANFiXzgA3W1fXEah77R/BgobL1SA8Rw1bVf/ZFHcr2aXmfpDSNKfxfqa4V' \ u'fWfTZU6E8Zi8mOQpNRI8TG3VfWfTc36XqrNTzdtq7z2y1G17wHFMH8Lkq85y1' \ u'Kz2peyP5Z/1eG1X4R69jkjRvhuNVyuJvKp3tiq+j1Qjxyz7K1y8f3Wr6pr39T' \ u'kJ6u7UYKZ7fE1Z3mq5phmJ1DMLYrcPL9/J6VII7oEkKclaH1dR6CsB6wPkvWU' \ u'JH8LuvZI1Qw5CMgg8hmRzyOEq7nPWZCa+3uY9rWpZsi+r12O+pVjwojKTOP/a' \ u'51yyimn9kL9ACOsApMnN2KuAAAAAElFTkSuQmCC' self.assertEquals(b64encode_and_unicode(create_png('Hallo')), png_b64) self.assertEquals(create_img('Hello', raw=True), u'data:image/png;base64,SGVsbG8=') self.assertEquals(create_img('Hallo'), u'data:image/png;base64,{0!s}'.format(png_b64))
def aes_encrypt_b64(key, data): """ This function encrypts the data using AES-128-CBC. It generates and adds an IV. This is used for PSKC. :param key: Encryption key (binary format) :type key: bytes :param data: Data to encrypt :type data: bytes :return: base64 encrypted output, containing IV and encrypted data :rtype: str """ iv = geturandom(16) encdata = aes_encrypt(key, iv, data) return b64encode_and_unicode(iv + encdata)
def get_as_dict(self): """ This returns the token data as a dictionary. It is used to display the token list at /token/list. The certificate token can add the PKCS12 file if it exists :return: The token data as dict :rtype: dict """ # first get the database values as dict token_dict = self.token.get() if "privatekey" in token_dict.get("info"): token_dict["info"]["pkcs12"] = b64encode_and_unicode(self._create_pkcs12_bin()) return token_dict
def aes_encrypt_b64(key, data): """ This function encrypts the data using AES-128-CBC. It generates and adds an IV. This is used for PSKC. :param key: Encryption key (binary format) :type key: bytes :param data: Data to encrypt :type data: bytes :return: base64 encrypted output, containing IV and encrypted data :rtype: str """ # pad data padder = padding.PKCS7(algorithms.AES.block_size).padder() padded_data = padder.update(data) + padder.finalize() iv = geturandom(16) encdata = aes_cbc_encrypt(key, iv, padded_data) return b64encode_and_unicode(iv + encdata)
def yubico_api_signature(data, api_key): """ Get a dictionary "data", sort the dictionary by the keys and sign it HMAC-SHA1 with the api_key :param data: The data to be signed :type data: dict :param api_key: base64 encoded API key :type api_key: basestring :return: base64 encoded signature """ r = dict(data) if 'h' in r: del r['h'] keys = sorted(r.keys()) data_string = "" for key in keys: data_string += "{0!s}={1!s}&".format(key, r.get(key)) data_string = data_string.strip("&") api_key_bin = base64.b64decode(api_key) # generate the signature h = hmac.new(api_key_bin, to_bytes(data_string), sha1).digest() h_b64 = b64encode_and_unicode(h) return h_b64
def export_pskc(tokenobj_list, psk=None): """ Take a list of token objects and create a beautifulsoup xml object. If no preshared key is given, we create one and return it. :param tokenobj_list: list of token objects :param psk: pre-shared-key for AES-128-CBC in hex format :return: tuple of (psk, number of tokens, beautifulsoup) """ if psk: psk = binascii.unhexlify(psk) else: psk = geturandom(16) mackey = geturandom(20) encrypted_mackey = aes_encrypt_b64(psk, mackey) number_of_exported_tokens = 0 # define the header soup = BeautifulSoup("""<KeyContainer Version="1.0" xmlns="urn:ietf:params:xml:ns:keyprov:pskc" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"> <EncryptionKey> <ds:KeyName>Pre-shared-key</ds:KeyName> </EncryptionKey> <MACMethod Algorithm="http://www.w3.org/2000/09/xmldsig#hmac-sha1"> <MACKey> <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc"/> <xenc:CipherData> <xenc:CipherValue>{encrypted_mackey}</xenc:CipherValue> </xenc:CipherData> </MACKey> </MACMethod> """.format(encrypted_mackey=encrypted_mackey), "html.parser") for tokenobj in tokenobj_list: if tokenobj.type.lower() not in ["totp", "hotp", "pw"]: continue type = tokenobj.type.lower() issuer = "privacyIDEA" try: manufacturer = tokenobj.token.description.encode("ascii", "replace") manufacturer = to_unicode(manufacturer) except UnicodeEncodeError: manufacturer = "deleted during export" serial = tokenobj.token.serial otplen = tokenobj.token.otplen counter = tokenobj.token.count suite = tokenobj.get_tokeninfo("hashlib", default="sha1") if type == "totp": timestep = tokenobj.get_tokeninfo("timeStep") timedrift = tokenobj.get_tokeninfo("timeShift") else: timestep = 0 timedrift = 0 otpkey = tokenobj.token.get_otpkey().getKey() try: if tokenobj.type.lower() in ["totp", "hotp"]: encrypted_otpkey = aes_encrypt_b64(psk, binascii.unhexlify(otpkey)) elif tokenobj.type.lower() in ["pw"]: encrypted_otpkey = aes_encrypt_b64(psk, otpkey) else: encrypted_otpkey = aes_encrypt_b64(psk, otpkey) hm = hmac.new(key=mackey, msg=otpkey, digestmod=hashlib.sha1) mac_value = b64encode_and_unicode(hm.digest()) except TypeError: # Some keys might be odd string length continue try: kp2 = BeautifulSoup("""<KeyPackage> <DeviceInfo> <Manufacturer>{manufacturer}</Manufacturer> <SerialNo>{serial}</SerialNo> </DeviceInfo> <Key Id="{serial}" Algorithm="urn:ietf:params:xml:ns:keyprov:pskc:{type}"> <Issuer>{issuer}</Issuer> <AlgorithmParameters> <ResponseFormat Length="{otplen}" Encoding="DECIMAL"/> <Suite hashalgo="{suite}" /> </AlgorithmParameters> <Data> <Secret> <EncryptedValue> <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc"/> <xenc:CipherData> <xenc:CipherValue>{encrypted_otpkey}</xenc:CipherValue> </xenc:CipherData> </EncryptedValue> </Secret> <ValueMAC>{value_mac}</ValueMAC> <Time> <PlainValue>0</PlainValue> </Time> <TimeInterval> <PlainValue>{timestep}</PlainValue> </TimeInterval> <Counter> <PlainValue>{counter}</PlainValue> </Counter> <TimeDrift> <PlainValue>{timedrift}</PlainValue> </TimeDrift> </Data> </Key> </KeyPackage>""".format(serial=html.escape(serial), type=html.escape(type), otplen=otplen, issuer=html.escape(issuer), manufacturer=html.escape(manufacturer), counter=counter, timestep=timestep, encrypted_otpkey=encrypted_otpkey, timedrift=timedrift, value_mac=mac_value, suite=html.escape(suite)), "html.parser") soup.macmethod.insert_after(kp2) number_of_exported_tokens += 1 except Exception as e: log.warning(u"Failed to export the token {0!s}: {1!s}".format(serial, e)) tb = traceback.format_exc() log.debug(tb) return hexlify_and_unicode(psk), number_of_exported_tokens, soup
def parsePSKCdata(xml_data, preshared_key_hex=None, password=None, validate_mac='check_fail_hard', do_checkserial=False): """ This function parses XML data of a PSKC file, (RFC6030) It can read * AES-128-CBC encrypted (preshared_key_bin) data * password based encrypted data * plain text data :param xml_data: The XML data :type xml_data: basestring :param preshared_key_hex: The preshared key, hexlified :param password: The password that encrypted the keys :param do_checkserial: Check if the serial numbers conform to the OATH specification (not yet implemented) :param validate_mac: Operation mode of hmac validation. Possible values: - 'check_fail_hard' : If an invalid hmac is encountered no token gets parsed. - 'check_fail_soft' : Skip tokens with invalid MAC. - 'no_check' : Hmac of tokens are not checked, every token is parsed. :return: tuple of a dictionary of token dictionaries and a list of serial of not imported tokens { serial : { otpkey , counter, .... }}, [serial, serial, ...] """ abort = False not_imported_serials = [] tokens = {} xml = strip_prefix_from_soup(BeautifulSoup(xml_data, "lxml")) if not xml.keycontainer: raise ImportException("No KeyContainer found in PSKC data. Could not " "import any tokens.") if xml.keycontainer.encryptionkey and \ xml.keycontainer.encryptionkey.derivedkey: # If we have a password we also need a tag EncryptionKey in the # KeyContainer preshared_key_hex = derive_key(xml, password) key_packages = xml.keycontainer.findAll("keypackage") for key_package in key_packages: token = {} key = key_package.key try: token["description"] = key_package.deviceinfo.manufacturer.string except Exception as exx: log.debug("Can not get manufacturer string {0!s}".format(exx)) serial = key["id"] try: serial = key_package.deviceinfo.serialno.string.strip() except Exception as exx: log.debug( "Can not get serial string from device info {0!s}".format(exx)) algo = key["algorithm"] token["type"] = algo.split(":")[-1].lower() parameters = key.algorithmparameters token["otplen"] = parameters.responseformat["length"] or 6 try: token["hashlib"] = parameters.suite["hashalgo"] or "sha1" except Exception as exx: log.warning("No compatible suite contained.") try: if key.data.secret.plainvalue: secret = key.data.secret.plainvalue.string token["otpkey"] = hexlify_and_unicode(base64.b64decode(secret)) elif key.data.secret.encryptedvalue: encryptionmethod = key.data.secret.encryptedvalue.encryptionmethod enc_algorithm = encryptionmethod["algorithm"].split("#")[-1] if enc_algorithm.lower() != "aes128-cbc": raise ImportException("We only import PSKC files with " "AES128-CBC.") enc_data = key.data.secret.encryptedvalue.ciphervalue.text enc_data = enc_data.strip() preshared_key = binascii.unhexlify(preshared_key_hex) secret = aes_decrypt_b64(preshared_key, enc_data) if token["type"].lower() in ["hotp", "totp"]: token["otpkey"] = hexlify_and_unicode(secret) elif token["type"].lower() in ["pw"]: token["otpkey"] = to_unicode(secret) else: token["otpkey"] = to_unicode(secret) if validate_mac != 'no_check': # Validate MAC: encrypted_mac_key = xml.keycontainer.find("mackey").text mac_key = aes_decrypt_b64(preshared_key, encrypted_mac_key) enc_data_bin = base64.b64decode(enc_data) hm = hmac.new(key=mac_key, msg=enc_data_bin, digestmod=hashlib.sha1) mac_value_calculated = b64encode_and_unicode(hm.digest()) mac_value_xml = key.data.find('valuemac').text.strip() is_invalid = not hmac.compare_digest( mac_value_xml, mac_value_calculated) if is_invalid and validate_mac == 'check_fail_hard': abort = True elif is_invalid and validate_mac == 'check_fail_soft': not_imported_serials.append(serial) continue except Exception as exx: log.error("Failed to import tokendata: {0!s}".format(exx)) log.debug(traceback.format_exc()) raise ImportException("Failed to import tokendata. Wrong " "encryption key? %s" % exx) if token["type"] in ["hotp", "totp"] and key.data.counter: token["counter"] = key.data.counter.text.strip() if token["type"] == "totp": if key.data.timeinterval: token["timeStep"] = key.data.timeinterval.text.strip() if key.data.timedrift: token["timeShift"] = key.data.timedrift.text.strip() tokens[serial] = token if abort: not_imported_serials = tokens.keys() tokens = {} # reset tokens return tokens, not_imported_serials