Exemplo n.º 1
0
def test_login():
    '''Performs a basic login intended to be successful'''

    dbconn = setup_test()
    dbdata = init_server(dbconn)
    conn = ServerConnection()
    assert conn.connect('localhost',
                        2001), "Connection to server at localhost:2001 failed"

    # password is 'SandstoneAgendaTricycle'
    pwhash = '$argon2id$v=19$m=65536,t=2,p=1$ew5lqHA5z38za+257DmnTA$0LWVrI2r7XCq' \
       'dcCYkJLok65qussSyhN5TTZP+OTgzEI'
    devid = '22222222-2222-2222-2222-222222222222'
    devpair = EncryptionPair(
        CryptoString(r'CURVE25519:@X~msiMmBq0nsNnn0%~x{M|NU_{?<Wj)cYybdh&Z'),
        CryptoString(r'CURVE25519:W30{oJ?w~NBbj{F8Ag4~<bcWy6_uQ{i{X?NDq4^l'))

    dbdata['pwhash'] = pwhash
    dbdata['devid'] = devid
    dbdata['devpair'] = devpair

    # Most of the code which was originally written for this test is needed for other tests
    # because commands like PREREG require being logged in as the administrator. Both of these
    # functions still perform all the necessary tests that were originally done here.
    regcode_admin(dbdata, conn)
    login_admin(dbdata, conn)

    conn.send_message({'Action': "QUIT"})
Exemplo n.º 2
0
def load_secretkey(path: str) -> RetVal:
	'''Instantiates a secret key from a file'''
	if not path:
		return RetVal(BadParameterValue, 'path may not be empty')
	
	if not os.path.exists(path):
		return RetVal(ResourceNotFound, '%s exists' % path)
	
	indata = None
	try:
		with open(path, "r") as fhandle:
			indata = json.load(fhandle)
	
	except Exception as e:
		return RetVal(ExceptionThrown, e)
	
	if not isinstance(indata, dict):
		return RetVal(BadData, 'File does not contain an Anselus JSON secret key')

	try:
		jsonschema.validate(indata, __secret_key_schema)
	except jsonschema.ValidationError:
		return RetVal(BadData, "file data does not validate")
	except jsonschema.SchemaError:
		return RetVal(InternalError, "BUG: invalid SecretKey schema")

	key = CryptoString(indata['SecretKey'])
	if not key.is_valid():
		return RetVal(BadData, 'Failure to base85 decode key data')
	
	return RetVal().set_value('key', SecretKey(key))
Exemplo n.º 3
0
    def chain(self, key: CryptoString, rotate_optional: bool) -> RetVal:
        '''Appends a new entry to the chain, optionally rotating keys which aren't required to be 
		changed. This method requires that the root entry already exist. Note that user cards will 
		not have all the required signatures when the call returns'''
        if len(self.entries) < 1:
            return RetVal(ResourceNotFound, 'missing root entry')

        # Just in case we get some squirrelly non-Org, non-User card type
        chain_method = getattr(self.entries[-1], "chain", None)
        if not chain_method or not callable(chain_method):
            return RetVal(FeatureNotAvailable,
                          "entry doesn't support chaining")

        chaindata = self.entries[-1].chain(key, rotate_optional)
        if chaindata.error():
            return chaindata

        new_entry = chaindata['entry']

        skeystring = CryptoString()
        status = skeystring.set(chaindata['sign.private'])
        if status.error():
            return status

        if new_entry.type == 'User':
            status = new_entry.sign(skeystring, 'User')
        else:
            status = new_entry.sign(skeystring, 'Organization')
        if status.error():
            return status

        chaindata['entry'] = new_entry
        self.entries.append(new_entry)
        return chaindata
Exemplo n.º 4
0
def signingpair_from_string(keystr : str) -> SigningPair:
	'''Intantiates a signing pair from a saved seed string that is used for the private key'''
	
	key = nacl.signing.SigningKey(base64.b85decode(keystr))
	return SigningPair(
		CryptoString('ED25519:' + base64.b85encode(key.verify_key.encode()).decode()),
		CryptoString('ED25519:' + base64.b85encode(key.encode()).decode())	
	)
Exemplo n.º 5
0
def test_setpassword():
    '''Tests the SETPASSWORD command'''
    dbconn = setup_test()
    dbdata = init_server(dbconn)
    conn = ServerConnection()
    assert conn.connect('localhost',
                        2001), "Connection to server at localhost:2001 failed"

    # password is 'SandstoneAgendaTricycle'
    pwhash = '$argon2id$v=19$m=65536,t=2,p=1$ew5lqHA5z38za+257DmnTA$0LWVrI2r7XCq' \
       'dcCYkJLok65qussSyhN5TTZP+OTgzEI'
    devid = '22222222-2222-2222-2222-222222222222'
    devpair = EncryptionPair(
        CryptoString(r'CURVE25519:@X~msiMmBq0nsNnn0%~x{M|NU_{?<Wj)cYybdh&Z'),
        CryptoString(r'CURVE25519:W30{oJ?w~NBbj{F8Ag4~<bcWy6_uQ{i{X?NDq4^l'))

    dbdata['pwhash'] = pwhash
    dbdata['devid'] = devid
    dbdata['devpair'] = devpair

    regcode_admin(dbdata, conn)
    login_admin(dbdata, conn)

    badpwhash = '$argon2id$v=19$m=65536,t=2,' \
     'p=1$l0nvPkQDxJDKIMZsA96x4A$c5+XHmZjzSJIg3/vXXr33YNB1Jl52mdXtbMASLP1abs'
    newpwhash = '$argon2id$v=19$m=65536,t=2,' \
     'p=1$4WOWeLn7oOmrkp41zMzMcQ$8G51UCiIC/eETPJf0RZ5cNkX7jr3NkT92etTwNEO+f0'

    # Subtest #1: Failure because of wrong password sent
    conn.send_message({
        'Action': "SETPASSWORD",
        'Data': {
            'Password-Hash': badpwhash,
            'NewPassword-Hash': newpwhash
        }
    })

    response = conn.read_response(None)
    assert response['Code'] == 402 and response['Status'] == 'AUTHENTICATION FAILURE', \
     'test_setpassword(): Failed to catch bad password'

    # Subtest #2: Successful password change
    conn.send_message({
        'Action': "SETPASSWORD",
        'Data': {
            'Password-Hash': pwhash,
            'NewPassword-Hash': newpwhash
        }
    })

    response = conn.read_response(None)
    assert response['Code'] == 200 and response['Status'] == 'OK', \
     'test_setpassword(): Failed to update admin password'

    conn.send_message({'Action': "QUIT"})
Exemplo n.º 6
0
def test_reset_password():
    '''Tests password reset code'''
    dbconn = setup_test()
    dbdata = init_server(dbconn)

    conn = serverconn.ServerConnection()
    status = conn.connect('localhost', 2001)
    assert not status.error(
    ), f"test_login(): failed to connect to server: {status.info()}"

    password = Password('Linguini2Pegboard*Album')
    devid = '14142135-9c22-4d3e-84a3-2aa281f65714'
    keypair = EncryptionPair(
        CryptoString(r'CURVE25519:mO?WWA-k2B2O|Z%fA`~s3^$iiN{5R->#jxO@cy6{'),
        CryptoString(r'CURVE25519:2bLf2vMA?GA2?L~tv<PA9XOw6e}V~ObNi7C&qek>'))
    status = serverconn.regcode(conn, 'admin', dbdata['admin_regcode'],
                                password.hashstring, devid, keypair, '')
    assert not status.error(
    ), f"test_reset_password(): regcode failed: {status.info()}"

    status = serverconn.login(conn, dbdata['admin_wid'],
                              CryptoString(dbdata['oekey']))
    assert not status.error(
    ), f"test_reset_password(): login phase failed: {status.info()}"

    status = serverconn.password(conn, dbdata['admin_wid'],
                                 password.hashstring)
    assert not status.error(
    ), f"test_reset_password(): password phase failed: {status.info()}"

    status = serverconn.device(conn, devid, keypair)
    assert not status.error(), "test_reset_password(): device phase failed: " \
     f"{status.info()}"

    status = init_user(conn, dbdata)
    assert not status.error(
    ), f"test_reset_password(): user init failed: {status.info()}"

    status = serverconn.reset_password(conn, dbdata['user_wid'])
    assert not status.error(
    ), f"test_reset_password(): password reset failed: {status.info()}"
    resetdata = status

    status = serverconn.logout(conn)
    assert not status.error(
    ), f"test_reset_password(): admin logout failed: {status.info()}"

    newpassword = Password('SomeOth3rPassw*rd')
    status = serverconn.passcode(conn, dbdata['user_wid'],
                                 resetdata['resetcode'],
                                 newpassword.hashstring)
    assert not status.error(
    ), f"test_reset_password(): passcode failed: {status.info()}"
Exemplo n.º 7
0
	def __init__(self, key=None):
		super().__init__()
		if key:
			if type(key).__name__ != 'CryptoString':
				raise TypeError
			self.key = key
		else:
			self.enctype = 'XSALSA20'
			self.key = CryptoString('XSALSA20:' + \
					base64.b85encode(nacl.utils.random(nacl.secret.SecretBox.KEY_SIZE)).decode())
		
		self.hash = blake2hash(self.key.data.encode())
Exemplo n.º 8
0
    def verify_hash(self) -> RetVal:
        '''Checks that the entry's actual hash matches that in the hash field'''
        current_hash = CryptoString(self.hash)
        if not current_hash.is_valid():
            return RetVal(InvalidHash,
                          f"{self.hash} is not a valid CryptoString")

        status = self.get_hash(current_hash.prefix)
        if status.error():
            return status

        return RetVal()
Exemplo n.º 9
0
def test_preregister_regcode():
    '''Test the preregister and regcode commands'''
    dbconn = setup_test()
    dbdata = init_server(dbconn)

    conn = serverconn.ServerConnection()
    status = conn.connect('localhost', 2001)
    assert not status.error(
    ), f"test_login(): failed to connect to server: {status.info()}"

    password = Password('Linguini2Pegboard*Album')
    devid = '14142135-9c22-4d3e-84a3-2aa281f65714'
    keypair = EncryptionPair(
        CryptoString(r'CURVE25519:mO?WWA-k2B2O|Z%fA`~s3^$iiN{5R->#jxO@cy6{'),
        CryptoString(r'CURVE25519:2bLf2vMA?GA2?L~tv<PA9XOw6e}V~ObNi7C&qek>'))
    status = serverconn.regcode(conn, 'admin', dbdata['admin_regcode'],
                                password.hashstring, devid, keypair, '')
    assert not status.error(
    ), f"test_preregister_regcode(): regcode failed: {status.info()}"

    status = serverconn.login(conn, dbdata['admin_wid'],
                              CryptoString(dbdata['oekey']))
    assert not status.error(
    ), f"test_preregister_regcode(): login phase failed: {status.info()}"

    status = serverconn.password(conn, dbdata['admin_wid'],
                                 password.hashstring)
    assert not status.error(
    ), f"test_preregister_regcode(): password phase failed: {status.info()}"

    status = serverconn.device(conn, devid, keypair)
    assert not status.error(), "test_preregister_regcode(): device phase failed: " \
     f"{status.info()}"

    status = serverconn.preregister(conn, '', 'csimons', 'example.net')
    assert not status.error(
    ), "test_preregister_regcode(): uid preregistration failed"
    assert status['domain'] == 'example.net' and 'wid' in status and 'regcode' in status and \
     status['uid'] == 'csimons', "test_preregister_regcode(): failed to return expected data"

    regdata = status
    password = Password('MyS3cretPassw*rd')
    devpair = EncryptionPair()
    status = serverconn.regcode(conn, 'csimons', regdata['regcode'],
                                password.hashstring,
                                '11111111-1111-1111-1111-111111111111',
                                devpair, 'example.net')
    assert not status.error(), "test_preregister_regcode(): uid regcode failed"

    conn.disconnect()
Exemplo n.º 10
0
    def __validate_org_data(self) -> RetVal:
        '''Checks the validity of all data fields'''

        if self.type != 'Organization':
            return RetVal(BadData, 'invalid entry type %s' % self.type)

        outStatus = self.__validate_common_data()
        if outStatus.error():
            return outStatus

        # Required field: Admin address
        m = re.match(
            r'^[\da-fA-F]{8}-?[\da-fA-F]{4}-?[\da-fA-F]{4}-?[\da-fA-F]{4}'
            r'-?[\da-fA-F]{12}/([a-zA-Z0-9]+\.)+[a-zA-Z0-9]+$',
            self.fields['Contact-Admin'])
        if not m:
            return RetVal(BadData, 'bad admin contact address')

        # Required fields: Primary Verification Key, Encryption Key
        # We can't verify the actual key data, but we can at least ensure that it's formatted
        # correctly and we can b85decode the key itself
        for keyfield in ['Primary-Verification-Key', 'Encryption-Key']:
            if not CryptoString(self.fields[keyfield]).is_valid():
                return RetVal(BadData, f"bad key field {keyfield}")

        # Optional fields: Support and Abuse addresses
        for contactfield in ['Contact-Support', 'Contact-Abuse']:
            if contactfield in self.fields.keys():
                m = re.match(
                    r'^[\da-fA-F]{8}-?[\da-fA-F]{4}-?[\da-fA-F]{4}-?[\da-fA-F]{4}'
                    r'-?[\da-fA-F]{12}/([a-zA-Z0-9]+\.)+[a-zA-Z0-9]+$',
                    self.fields[contactfield])
                if not m:
                    return RetVal(BadData,
                                  f"bad contact address {contactfield}")

        # Optional field: Language
        if 'Language' in self.fields.keys():
            m = re.match(r'^[a-zA-Z]{2,3}(,[a-zA-Z]{2,3})*?$',
                         self.fields['Language'])
            if not m:
                return RetVal(BadData, 'bad language list')

        # Optional field: Secondary Verification Key
        if 'Secondary-Verification-Key' in self.fields.keys():
            if not CryptoString(
                    self.fields['Secondary-Verification-Key']).is_valid():
                return RetVal(BadData, 'bad secondary verification key')

        return RetVal()
Exemplo n.º 11
0
    def verify_chain(self, previous: EntryBase) -> RetVal:
        '''Verifies the chain of custody between the provided previous entry and the current one.'''

        if previous.type != 'Organization':
            return RetVal(BadParameterValue, 'entry type mismatch')

        if 'Custody' not in self.signatures or not self.signatures['Custody']:
            return RetVal(ResourceNotFound, 'custody signature missing')

        if 'Primary-Verification-Key' not in previous.fields or \
          not previous.fields['Primary-Verification-Key']:
            return RetVal(ResourceNotFound, 'signing key missing')

        try:
            prev_index = int(previous['Index'])
        except:
            return RetVal(BadData, 'previous entry has a bad index')

        try:
            index = int(self['Index'])
        except:
            return RetVal(BadData, 'current entry has a bad index')

        if index != prev_index + 1:
            return RetVal(InvalidKeycard, 'entry index compliance failure')

        status = self.verify_signature(
            CryptoString(previous.fields['Primary-Verification-Key']),
            'Custody')
        return status
Exemplo n.º 12
0
def test_secretkey_save():
    '''Tests the save code of the SecretKey class'''
    test_folder = setup_test('encryption_secretkey_save')

    key = CryptoString(r"XSALSA20:J~T^ko3HCFb$1Z7NudpcJA-dzDpF52IF1Oysh+CY")
    sk = encryption.SecretKey(key)

    key_path = os.path.join(test_folder, 'testkey.jk')
    status = sk.save(key_path)
    assert not status.error(), "Failed to create saved encryption pair file"

    fhandle = open(key_path)
    filedata = json.load(fhandle)
    fhandle.close()

    assert filedata['SecretKey'] == key.as_string(
    ), "Saved data does not match input data"
Exemplo n.º 13
0
def test_encryptionpair_encrypt_decrypt():
    '''Test the encryption and decryption code for the EncryptionPair class'''

    public_key = CryptoString(
        r"CURVE25519:(B2XX5|<+lOSR>_0mQ=KX4o<aOvXe6M`Z5ldINd`")
    private_key = CryptoString(
        r"CURVE25519:(Rj5)mmd1|YqlLCUP0vE;YZ#o;tJxtlAIzmPD7b&")
    kp = encryption.EncryptionPair(public_key, private_key)

    test_data = 'This is some encryption test data'
    estatus = kp.encrypt(test_data.encode())
    assert not estatus.error(
    ), 'test_encryptionpair_encrypt_decrypt: error encrypting test data'

    dstatus = kp.decrypt(estatus['data'])
    assert not dstatus.error(
    ), 'test_encryptionpair_encrypt_decrypt: error decrypting test data'
    assert dstatus['data'] == test_data, 'decoded data mismatch'
Exemplo n.º 14
0
def test_user_chaining():
    '''Tests chaining of user entries and verification thereof'''
    userentry = make_test_userentry()

    # User contact request signing key
    crskeystring = CryptoString(
        'ED25519:ip52{ps^jH)t$k-9bc_RzkegpIW?}FFe~BX&<V}9')

    # Organization signing key
    oskeystring = CryptoString(
        'ED25519:msvXw(nII<Qm6oBHc+92xwRI3>VFF-RcZ=7DEu3|')

    chaindata = userentry.chain(crskeystring, True)
    assert not chaindata.error(
    ), f'userentry.chain returned an error: {chaindata.error()}'

    new_entry = chaindata['entry']

    # Now that we have a new entry, it only has a valid custody signature. Add all the other
    # signatures needed to be compliant and then verify the whole thing.

    # The signing key is replaced during chain()
    new_crskeystring = CryptoString()
    status = new_crskeystring.set(chaindata['crsign.private'])
    assert not status.error(
    ), 'test_user_chain: new signing key has bad format'

    status = new_entry.sign(oskeystring, 'Organization')
    assert not status.error(), f'new entry failed to org sign: {status}'

    new_entry.prev_hash = userentry.hash
    status = new_entry.generate_hash('BLAKE2B-256')
    assert not status.error(), f'new entry failed to hash: {status}'

    status = new_entry.sign(new_crskeystring, 'User')
    assert not status.error(), f'new entry failed to user sign: {status}'

    status = new_entry.is_compliant()
    assert not status.error(), f'new entry failed compliance check: {status}'

    # Testing of chain() is complete. Now test verify_chain()
    status = new_entry.verify_chain(userentry)
    assert not status.error(
    ), f'chain of custody verification failed: {status}'
Exemplo n.º 15
0
def test_keycard_chain_verify_load_save():
    '''Tests entry rotation of a keycard'''
    userentry = make_test_userentry()

    # User contact request signing and verification keys
    crskeystring = CryptoString(
        'ED25519:ip52{ps^jH)t$k-9bc_RzkegpIW?}FFe~BX&<V}9')

    card = keycard.Keycard()
    card.entries.append(userentry)

    chaindata = card.chain(crskeystring, True)
    assert not chaindata.error(), f'keycard chain failed: {chaindata}'

    new_entry = chaindata['entry']
    oskeystring = CryptoString(
        'ED25519:msvXw(nII<Qm6oBHc+92xwRI3>VFF-RcZ=7DEu3|')

    status = new_entry.sign(oskeystring, 'Organization')
    assert not status.error(), f'chained entry failed to org sign: {status}'

    new_entry.prev_hash = userentry.hash
    new_entry.generate_hash('BLAKE2B-256')
    assert not status.error(), f'chained entry failed to hash: {status}'

    skeystring = CryptoString(chaindata['sign.private'])
    status = new_entry.sign(skeystring, 'User')
    assert not status.error(), f'chained entry failed to user sign: {status}'

    card.entries[-1] = new_entry
    status = card.verify()
    assert not status.error(), f'keycard failed to verify: {status}'

    # Although it doesn't make a lot of initial sense to group saving and loading tests with
    # code that handles chaining and verification, it saves on a lot of duplicate test code
    test_folder = setup_test('keycard_save')
    status = card.save(os.path.join(test_folder, 'user_save_test_keycard.kc'),
                       True)
    assert not status.error(), f'keycard failed to save: {status}'

    newcard = keycard.Keycard()
    status = newcard.load(
        os.path.join(test_folder, 'user_save_test_keycard.kc'))
    assert not status.error(), f'keycard failed to load: {status}'
Exemplo n.º 16
0
    def __validate_user_data(self) -> RetVal:
        '''Checks the validity of all data fields'''
        if self.type != 'User':
            return RetVal(BadData, 'invalid entry type %s' % self.type)

        outStatus = self.__validate_common_data()
        if outStatus.error():
            return outStatus

        # Required field: Workspace ID
        m = re.match(
            r'^[\da-fA-F]{8}-?[\da-fA-F]{4}-?[\da-fA-F]{4}-?[\da-fA-F]{4}'
            r'-?[\da-fA-F]{12}$', self.fields['Workspace-ID'])
        if not m:
            return RetVal(BadData, 'bad workspace ID')

        # Required field: Domain
        # Although mostly freeform, the Name field has a couple requirements:
        m = re.match(r'([a-zA-Z0-9]+\.)+[a-zA-Z0-9]+', self.fields['Domain'])
        if not m or len(self.fields['Domain']) >= 64:
            return RetVal(BadData, 'bad domain value')

        # Required fields: Contact Request Verification Key, Contact Request Encryption Key,
        #	Public-Encryption-Key
        for keyfield in [
                'Contact-Request-Verification-Key',
                'Contact-Request-Encryption-Key', 'Public-Encryption-Key'
        ]:
            if not CryptoString(self.fields[keyfield]).is_valid():
                return RetVal(BadData, f"bad key field {keyfield}")

        # Optional field: User ID
        if 'User-ID' in self.fields.keys():
            if re.findall(r'[\\\/\s"]', self.fields['User-ID']) or \
             len(self.fields['User-ID']) >= 64:
                return RetVal(BadData, 'bad user id value')

        if 'Alternate-Encryption-Key' in self.fields.keys():
            if not CryptoString(
                    self.fields['Alternate-Encryption-Key']).is_valid():
                return RetVal(BadData, 'bad alternate encryption key')

        return RetVal()
Exemplo n.º 17
0
    def get_hash(self, algorithm: str) -> RetVal:
        '''Generates a hash containing the expected signatures and the previous hash, if it exists. 
		The supported hash algorithms are 'BLAKE2-256', 'BLAKE3-256', 'SHA-256', and 'SHA3-256'.'''
        # if algorithm not in ['BLAKE3-256','BLAKE2B-256','SHA-256','SHA3-256']:
        if algorithm not in ['BLAKE2B-256', 'SHA-256', 'SHA3-256']:
            return RetVal(UnsupportedHashType,
                          f'{algorithm} not a supported hash algorithm')

        hash_string = CryptoString()
        hash_level = -1
        for sig in self.signature_info:
            if sig['type'] == SIGINFO_HASH:
                hash_level = sig['level']
                break
        assert hash_level > 0, "BUG: signature_info missing hash entry"

        # if algorithm == 'BLAKE3-256':
        # 	hasher = blake3.blake3() # pylint: disable=c-extension-no-member
        # 	hasher.update(self.make_bytestring(hash_level))
        # 	hash_string.data = base64.b85encode(hasher.digest()).decode()
        # else:
        # hasher = None
        # if algorithm == 'BLAKE2B-256':
        # 	hasher = hashlib.blake2b(digest_size=32)
        # elif algorithm == 'SHA-256':
        # 	hasher = hashlib.sha256()
        # else:
        # 	hasher = hashlib.sha3_256()
        # hasher.update(self.make_bytestring(hash_level))
        # hash_string.data = base64.b85encode(hasher.digest()).decode()
        hasher = None
        if algorithm == 'BLAKE2B-256':
            hasher = hashlib.blake2b(digest_size=32)
        elif algorithm == 'SHA-256':
            hasher = hashlib.sha256()
        else:
            hasher = hashlib.sha3_256()
        hasher.update(self.make_bytestring(hash_level))
        hash_string.data = base64.b85encode(hasher.digest()).decode()

        hash_string.prefix = algorithm
        return RetVal().set_value('hash', str(hash_string))
Exemplo n.º 18
0
	def __init__(self, public=None, private=None):
		super().__init__()
		if public and private:
			if not isinstance(public, CryptoString) or not isinstance(private, CryptoString):
				raise TypeError
			
			if public.prefix != private.prefix:
				raise ValueError
			
			self.enctype = public.prefix
			self.public = public
			self.private = private
		else:
			key = nacl.public.PrivateKey.generate()
			self.enctype = 'CURVE25519'
			self.public = CryptoString('CURVE25519:' + \
					base64.b85encode(key.public_key.encode()).decode())
			self.private = CryptoString('CURVE25519:' + \
					base64.b85encode(key.encode()).decode())
		self.pubhash = blake2hash(self.public.data.encode())
		self.privhash = blake2hash(self.private.data.encode())
Exemplo n.º 19
0
def test_signpair_sign_verify():
    '''Tests SigningPair's sign() and verify() methods'''

    public_key = CryptoString(
        r"ED25519:PnY~pK2|;AYO#1Z;B%T$2}E$^kIpL=>>VzfMKsDx")
    private_key = CryptoString(
        r"ED25519:{^A@`5N*T%5ybCU%be892x6%*Rb2rnYd=SGeO4jF")
    sp = encryption.SigningPair(public_key, private_key)

    key = nacl.signing.SigningKey(private_key.raw_data())
    signed = key.sign(b'1234567890', encryption.Base85Encoder)

    sstatus = sp.sign(b'1234567890')
    assert not sstatus.error(
    ), f"test_signpair_sign_verify: signing failed: {sstatus.info()}"
    assert sstatus['signature'] == 'ED25519:' + signed.signature.decode(), \
     "test_signpair_sign_verify: signature data mismatch"

    vstatus = sp.verify(b'1234567890', CryptoString(sstatus['signature']))
    assert not vstatus.error(
    ), f"test_signpair_sign_verify: verification failed: {vstatus.info()}"
Exemplo n.º 20
0
    def verify_signature(self, verify_key: CryptoString,
                         sigtype: str) -> RetVal:
        '''Verifies a signature, given a verification key'''

        if not verify_key.is_valid():
            return RetVal(BadParameterValue, 'bad verify key')

        sig_names = [x['name'] for x in self.signature_info]
        if sigtype not in sig_names:
            return RetVal(BadParameterValue, 'bad signature type')

        if verify_key.prefix != 'ED25519':
            return RetVal(UnsupportedEncryptionType, verify_key.prefix)

        if sigtype in self.signatures and not self.signatures[sigtype]:
            return RetVal(NotCompliant, 'empty signature ' + sigtype)

        sig = CryptoString()
        status = sig.set(self.signatures[sigtype])
        if status.error():
            return status

        try:
            vkey = nacl.signing.VerifyKey(verify_key.raw_data())
        except Exception as e:
            return RetVal(ExceptionThrown, e)

        try:
            data = self.make_bytestring(sig_names.index(sigtype))
            vkey.verify(data, sig.raw_data())
        except nacl.exceptions.BadSignatureError:
            return RetVal(InvalidKeycard)

        return RetVal()
Exemplo n.º 21
0
def load_encryptionpair(path: str) -> RetVal:
	'''Instantiates a keypair from a file'''
	if not path:
		return RetVal(BadParameterValue, 'path may not be empty')
	
	if not os.path.exists(path):
		return RetVal(ResourceNotFound, '%s exists' % path)
	
	indata = None
	try:
		with open(path, "r") as fhandle:
			indata = json.load(fhandle)
	
	except Exception as e:
		return RetVal(ExceptionThrown, e)
	
	if not isinstance(indata, dict):
		return RetVal(BadData, 'File does not contain an Anselus JSON keypair')

	try:
		jsonschema.validate(indata, __encryption_pair_schema)
	except jsonschema.ValidationError:
		return RetVal(BadData, "file data does not validate")
	except jsonschema.SchemaError:
		return RetVal(InternalError, "BUG: invalid EncryptionPair schema")

	public_key = CryptoString(indata['PublicKey'])
	private_key = CryptoString(indata['PrivateKey'])
	if not public_key.is_valid() or not private_key.is_valid():
		return RetVal(BadData, 'Failure to base85 decode key data')
	
	return RetVal().set_value('keypair', EncryptionPair(public_key, private_key))
Exemplo n.º 22
0
def test_overflow():
    '''Tests the server's command handling for commands greater than 8K'''

    dbconn = setup_test()
    dbdata = init_server(dbconn)
    conn = serverconn.ServerConnection()
    assert conn.connect('localhost',
                        2001), "Connection to server at localhost:2001 failed"

    # password is 'SandstoneAgendaTricycle'
    pwhash = '$argon2id$v=19$m=65536,t=2,p=1$ew5lqHA5z38za+257DmnTA$0LWVrI2r7XCq' \
       'dcCYkJLok65qussSyhN5TTZP+OTgzEI'
    devid = '22222222-2222-2222-2222-222222222222'
    devpair = EncryptionPair(
        CryptoString(r'CURVE25519:@X~msiMmBq0nsNnn0%~x{M|NU_{?<Wj)cYybdh&Z'),
        CryptoString(r'CURVE25519:W30{oJ?w~NBbj{F8Ag4~<bcWy6_uQ{i{X?NDq4^l'))

    dbdata['pwhash'] = pwhash
    dbdata['devid'] = devid
    dbdata['devpair'] = devpair

    regcode_admin(dbdata, conn)
    login_admin(dbdata, conn)

    conn.send_message({
        'Action': "REGISTER",
        'Data': {
            'Workspace-ID': 'A' * 10240,
            'Password-Hash': pwhash,
            'Device-ID': '11111111-1111-1111-1111-111111111111',
            'Device-Key': 'CURVE25519:@X~msiMmBq0nsNnn0%~x{M|NU_{?<Wj)cYybdh&Z'
        }
    })

    response = conn.read_response(server_response)
    assert response['Code'] == 400 and response['Status'] == 'BAD REQUEST', \
     'test_overflow: failed to catch overflow'

    conn.send_message({'Action': "QUIT"})
Exemplo n.º 23
0
	def __init__(self, public=None, private=None):
		super().__init__()

		if public and private:
			if type(public).__name__ != 'CryptoString' or \
				type(private).__name__ != 'CryptoString':
				raise TypeError
			
			if public.prefix != private.prefix:
				raise ValueError
			
			self.enctype = public.prefix
			self.public = public
			self.private = private
		else:
			key = nacl.signing.SigningKey.generate()
			self.enctype = 'ED25519'
			self.public = CryptoString('ED25519:' + \
					base64.b85encode(key.verify_key.encode()).decode())
			self.private = CryptoString('ED25519:' + \
					base64.b85encode(key.encode()).decode())		
		self.pubhash = blake2hash(self.public.data.encode())
		self.privhash = blake2hash(self.private.data.encode())
Exemplo n.º 24
0
    def sign(self, signing_key: CryptoString, sigtype: str) -> RetVal:
        '''Adds a signature to the  Note that for any change in the keycard fields, this 
		call must be made afterward. Note that successive signatures are deleted, such that 
		updating a User signature will delete the Organization signature which depends on it. The 
		sigtype must be Custody, User, or Organization, and the type is case-sensitive.'''
        if not signing_key.is_valid():
            return RetVal(BadParameterValue, 'signing key')

        if signing_key.prefix != 'ED25519':
            return RetVal(UnsupportedEncryptionType, signing_key.prefix)

        sig_names = [x['name'] for x in self.signature_info]
        if sigtype not in sig_names:
            return RetVal(BadParameterValue, 'sigtype')

        key = nacl.signing.SigningKey(signing_key.raw_data())

        # Clear all signatures which follow the current one. This expects that the signature_info
        # field lists the signatures in the order that they are required to appear.
        clear_sig = False
        sigtype_index = 0

        # We really do need to use an index here instead of just an iterator. Sheesh.
        for i in range(len(sig_names)):  # pylint: disable=consider-using-enumerate
            name = sig_names[i]
            if name == sigtype:
                clear_sig = True
                sigtype_index = i

            if clear_sig:
                self.signatures[name] = ''

        data = self.make_bytestring(sigtype_index + 1)
        signed = key.sign(data, Base85Encoder)
        self.signatures[sigtype] = 'ED25519:' + signed.signature.decode()
        return RetVal()
Exemplo n.º 25
0
def test_signpair_load():
    '''Tests the load code of the SigningPair class'''
    test_folder = setup_test('encryption_signpair_load')

    public_key = CryptoString(
        r"ED25519:PnY~pK2|;AYO#1Z;B%T$2}E$^kIpL=>>VzfMKsDx")
    private_key = CryptoString(
        r"ED25519:{^A@`5N*T%5ybCU%be892x6%*Rb2rnYd=SGeO4jF")
    kp = encryption.SigningPair(public_key, private_key)

    keypair_path = os.path.join(test_folder, 'testpair.jk')
    status = kp.save(keypair_path)
    assert not status.error(
    ), f"Failed to create saved signing pair file: {status.info()}"

    status = encryption.load_signingpair(keypair_path)
    assert not status.error(
    ), f"Failed to load saved signing pair file: {status.info()}"

    testpair = status['keypair']

    assert testpair.enctype == kp.enctype, "Loaded data does not match input data"
    assert testpair.public == public_key, "Loaded data does not match input data"
    assert testpair.private == private_key, "Loaded data does not match input data"
Exemplo n.º 26
0
def test_encryptionpair_load():
    '''Tests the load code of the EncryptionPair class'''
    test_folder = setup_test('encryption_encryptionpair_load')

    public_key = CryptoString(
        "CURVE25519:(B2XX5|<+lOSR>_0mQ=KX4o<aOvXe6M`Z5ldINd`")
    private_key = CryptoString(
        "CURVE25519:(Rj5)mmd1|YqlLCUP0vE;YZ#o;tJxtlAIzmPD7b&")
    kp = encryption.EncryptionPair(public_key, private_key)

    keypair_path = os.path.join(test_folder, 'testpair.jk')
    status = kp.save(keypair_path)
    assert not status.error(
    ), f"Failed to create saved encryption pair file: {status.info()}"

    status = encryption.load_encryptionpair(keypair_path)
    assert not status.error(
    ), f"Failed to load saved pair file: {status.info()}"

    testpair = status['keypair']

    assert testpair.enctype == kp.enctype, "Loaded data does not match input data"
    assert testpair.public == public_key, "Loaded data does not match input data"
    assert testpair.private == private_key, "Loaded data does not match input data"
Exemplo n.º 27
0
def test_devkey():
    '''Tests the devkey() command'''
    dbconn = setup_test()
    dbdata = init_server(dbconn)

    conn = serverconn.ServerConnection()
    status = conn.connect('localhost', 2001)
    assert not status.error(
    ), f"test_devkey(): failed to connect to server: {status.info()}"

    status = init_admin(conn, dbdata)
    assert not status.error(
    ), f"test_devkey(): init_admin failed: {status.info()}"

    newdevpair = EncryptionPair(
        CryptoString(r'CURVE25519:mO?WWA-k2B2O|Z%fA`~s3^$iiN{5R->#jxO@cy6{'),
        CryptoString(r'CURVE25519:2bLf2vMA?GA2?L~tv<PA9XOw6e}V~ObNi7C&qek>'))

    status = serverconn.devkey(conn, dbdata['admin_devid'],
                               dbdata['admin_devpair'], newdevpair)
    assert not status.error(
    ), f"test_devkey(): error returned: {status.info()}"

    conn.disconnect()
Exemplo n.º 28
0
def make_test_orgentry() -> keycard.OrgEntry:
    '''Generates an organizational entry for testing purposes'''

    # Primary signing key
    pskey = nacl.signing.SigningKey(
        b'msvXw(nII<Qm6oBHc+92xwRI3>VFF-RcZ=7DEu3|', Base85Encoder)

    orgcard = keycard.OrgEntry()
    orgcard.set_fields({
        'Name':
        'Acme Widgets, Inc.',
        'Contact-Admin':
        'c590b44c-798d-4055-8d72-725a7942f3f6/acme.com',
        'Language':
        'en',
        'Domain':
        'acme.com',
        'Primary-Verification-Key':
        'ED25519:)8id(gE02^S<{3H>9B;X4{DuYcb`%wo^mC&1lN88',
        'Encryption-Key':
        'CURVE25519:@b?cjpeY;<&y+LSOA&yUQ&ZIrp(JGt{W$*V>ATLG'
    })

    # Organization sign, hash, and verify

    rv = orgcard.generate_hash('BLAKE2B-256')
    assert not rv.error(), 'entry failed to hash'

    pskeystring = CryptoString()
    pskeystring.set('ED25519:' + base64.b85encode(pskey.encode()).decode())
    rv = orgcard.sign(pskeystring, 'Organization')
    assert not rv.error(), 'Unexpected RetVal error %s' % rv.error()
    assert orgcard.signatures['Organization'], 'entry failed to user sign'

    ovkey = nacl.signing.VerifyKey(pskey.verify_key.encode())
    ovkeystring = CryptoString()
    ovkeystring.prefix = 'ED25519'
    ovkeystring.data = base64.b85encode(ovkey.encode()).decode()

    rv = orgcard.verify_signature(ovkeystring, 'Organization')
    assert not rv.error(), 'org entry failed to verify'

    status = orgcard.is_compliant()
    assert not status.error(), f"OrgEntry wasn't compliant: {str(status)}"

    return orgcard
Exemplo n.º 29
0
	def verify(self, data : bytes, data_signature : CryptoString) -> RetVal:
		'''Return a Base85-encoded signature for the supplied data in the field 'signature'.'''
		
		if not isinstance(data, bytes):
			return RetVal(BadParameterType, 'bytes expected for data')
		if not isinstance(data_signature, CryptoString):
			return RetVal(BadParameterType, 'signature parameter must be a CryptoString')
		
		key = nacl.signing.VerifyKey(self.public.raw_data())

		try:
			key.verify(data, data_signature.raw_data())
		except Exception as e:
			return RetVal(VerificationError, e)
		
		return RetVal()
Exemplo n.º 30
0
def test_is_compliant_org():
    '''Tests compliance testing for the OrgEntry class'''

    # Organization signing key
    oskey = nacl.signing.SigningKey(
        b'msvXw(nII<Qm6oBHc+92xwRI3>VFF-RcZ=7DEu3|', Base85Encoder)

    orgcard = keycard.OrgEntry()
    orgcard.set_fields({
        'Name':
        'Acme Widgets, Inc',
        'Contact-Admin':
        '54025843-bacc-40cc-a0e4-df48a099c2f3/example.com',
        'Contact-Abuse':
        '54025843-bacc-40cc-a0e4-df48a099c2f3/example.com',
        'Language':
        'en',
        'Primary-Verification-Key':
        'ED25519:7dfD==!Jmt4cDtQDBxYa7(dV|N$}8mYwe$=RZuW|',
        'Encryption-Key':
        'CURVE25519:_`UC|vltn_%P5}~vwV^)oY){#uvQSSy(dOD_l(yE'
    })

    # sign and verify

    okeystring = CryptoString()
    okeystring.set('ED25519:' + base64.b85encode(oskey.encode()).decode())
    rv = orgcard.sign(okeystring, 'Organization')
    assert not rv.error(), 'Unexpected RetVal error %s' % rv.error()
    assert orgcard.signatures['Organization'], 'entry failed to user sign'

    ovkey = nacl.signing.VerifyKey(oskey.verify_key.encode())
    ovkeystring = CryptoString()
    ovkeystring.prefix = 'ED25519'
    ovkeystring.data = base64.b85encode(ovkey.encode()).decode()

    rv = orgcard.verify_signature(ovkeystring, 'Organization')
    assert not rv.error(), 'entry failed to org verify'

    rv = orgcard.generate_hash('BLAKE2B-256')
    assert not rv.error(), 'entry failed to hash'

    status = orgcard.is_compliant()
    assert not status.error(), f"OrgEntry wasn't compliant: {str(status)}"