Beispiel #1
0
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)
Beispiel #2
0
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_')
Beispiel #4
0
    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))
Beispiel #7
0
 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))
Beispiel #8
0
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
Beispiel #10
0
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)
Beispiel #11
0
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
Beispiel #12
0
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
Beispiel #13
0
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
Beispiel #14
0
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