Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@ python:
- "2.6"
- "2.7"
- "3.3"
- "3.4"
before_install:
- "sudo apt-get update"
- "sudo apt-get install swig libnettle4"
install:
install:
- "pip install -r requirements.txt -r requirements-tests.txt --use-mirrors"
- "if [[ ${TRAVIS_PYTHON_VERSION::1} != '3' ]]; then pip install -r requirements-tests-py2.txt --use-mirrors; fi"
- "if [[ ${TRAVIS_PYTHON_VERSION::1} = '3' ]]; then pip install -r requirements-tests-py3.txt --use-mirrors; fi"
script: "nosetests -v"
script: "py.test -v"
17 changes: 0 additions & 17 deletions onepassword/_pbkdf2_pycrypto.py

This file was deleted.

67 changes: 44 additions & 23 deletions onepassword/crypt_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,19 @@
import math
import struct

import Crypto.Cipher.AES
import Crypto.Hash.HMAC

from . import padding
from . import pbkdf1
from . import pbkdf2
from .hashes import MD5, SHA256, SHA512
from .util import make_utf8

from cryptography.exceptions import InvalidSignature
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives.hashes import MD5, SHA256, SHA512, Hash
from cryptography.hazmat.primitives.hmac import HMAC

_backend = default_backend()


# 8 bytes for "opdata1"
# 8 bytes for plaintext length
Expand Down Expand Up @@ -55,8 +59,11 @@ def a_decrypt_key(key_obj, password, aes_size=A_AES_SIZE):
keys = pbkdf2.pbkdf2_sha1(password=password, salt=salt, length=2*key_size, iterations=iterations)
key = keys[:key_size]
iv = keys[key_size:]
aes_er = Crypto.Cipher.AES.new(key, Crypto.Cipher.AES.MODE_CBC, iv)
potential_key = padding.pkcs5_unpad(aes_er.decrypt(data))

aes = Cipher(algorithms.AES(key), modes.CBC(iv), backend=_backend)
decryptor = aes.decryptor()
potential_key = padding.pkcs5_unpad(decryptor.update(data) + decryptor.finalize())

validation = base64.b64decode(key_obj['validation'])
decrypted_validation = a_decrypt_item(validation, potential_key)
if decrypted_validation != potential_key:
Expand Down Expand Up @@ -88,10 +95,14 @@ def a_decrypt_item(data, key, aes_size=A_AES_SIZE):
nkey = pb_gen.read(key_size)
iv = pb_gen.read(key_size)
else:
nkey = MD5.new(key).digest()
digest = Hash(MD5(), backend=_backend)
digest.update(key)
nkey = digest.finalize()
iv = '\x00'*key_size
aes_er = Crypto.Cipher.AES.new(nkey, Crypto.Cipher.AES.MODE_CBC, iv)
return padding.pkcs5_unpad(aes_er.decrypt(data))

aes = Cipher(algorithms.AES(nkey), modes.CBC(iv), backend=_backend)
decryptor = aes.decryptor()
return padding.pkcs5_unpad(decryptor.update(data) + decryptor.finalize())


def opdata1_unpack(data):
Expand All @@ -118,11 +129,15 @@ def opdata1_decrypt_key(data, key, hmac_key, aes_size=C_AES_SIZE, ignore_hmac=Fa
key_size = KEY_SIZE[aes_size]
iv, cryptext, expected_hmac = struct.unpack("=16s64s32s", data)
if not ignore_hmac:
verifier = Crypto.Hash.HMAC.new(key=hmac_key, msg=(iv + cryptext), digestmod=SHA256)
if verifier.digest() != expected_hmac:
verifier = HMAC(hmac_key, SHA256(), backend=_backend)
verifier.update(iv + cryptext)
try:
verifier.verify(expected_hmac)
except InvalidSignature:
raise ValueError("HMAC did not match for opdata1 key")
decryptor = Crypto.Cipher.AES.new(key, Crypto.Cipher.AES.MODE_CBC, iv)
decrypted = decryptor.decrypt(cryptext)
aes = Cipher(algorithms.AES(key), modes.CBC(iv), backend=_backend)
decryptor = aes.decryptor()
decrypted = decryptor.update(cryptext) + decryptor.finalize()
crypto_key, mac_key = decrypted[:key_size], decrypted[key_size:]
return crypto_key, mac_key

Expand All @@ -132,7 +147,9 @@ def opdata1_decrypt_master_key(data, key, hmac_key, aes_size=C_AES_SIZE, ignore_
bare_key = opdata1_decrypt_item(data, key, hmac_key, aes_size=aes_size, ignore_hmac=ignore_hmac)
# XXX: got the following step from jeff@agilebits (as opposed to the
# docs anywhere)
hashed_key = SHA512.new(bare_key).digest()
digest = Hash(SHA512(), backend=_backend)
digest.update(bare_key)
hashed_key = digest.finalize()
return hashed_key[:key_size], hashed_key[key_size:]


Expand All @@ -142,17 +159,20 @@ def opdata1_decrypt_item(data, key, hmac_key, aes_size=C_AES_SIZE, ignore_hmac=F
assert len(data) >= OPDATA1_MINIMUM_SIZE
plaintext_length, iv, cryptext, expected_hmac, hmac_d_data = opdata1_unpack(data)
if not ignore_hmac:
verifier = Crypto.Hash.HMAC.new(key=hmac_key, msg=hmac_d_data, digestmod=SHA256)
got_hmac = verifier.digest()
if len(got_hmac) != len(expected_hmac):
verifier = HMAC(hmac_key, SHA256(), backend=_backend)
verifier.update(hmac_d_data)
if len(verifier.copy().finalize()) != len(expected_hmac):
raise ValueError("Got unexpected HMAC length (expected %d bytes, got %d bytes)" % (
len(expected_hmac),
len(got_hmac)
))
if got_hmac != expected_hmac:
try:
verifier.verify(expected_hmac)
except InvalidSignature:
raise ValueError("HMAC did not match for opdata1 record")
decryptor = Crypto.Cipher.AES.new(key, Crypto.Cipher.AES.MODE_CBC, iv)
decrypted = decryptor.decrypt(cryptext)
aes = Cipher(algorithms.AES(key), modes.CBC(iv), backend=_backend)
decryptor = aes.decryptor()
decrypted = decryptor.update(cryptext) + decryptor.finalize()
unpadded = padding.ab_unpad(decrypted, plaintext_length)
return unpadded

Expand All @@ -171,7 +191,7 @@ def opdata1_derive_keys(password, salt, iterations=1000, aes_size=C_AES_SIZE):


def opdata1_verify_overall_hmac(hmac_key, item):
verifier = Crypto.Hash.HMAC.new(key=hmac_key, digestmod=SHA256)
verifier = HMAC(hmac_key, SHA256(), backend=_backend)
for key, value in sorted(item.items()):
if key == 'hmac':
continue
Expand All @@ -182,6 +202,7 @@ def opdata1_verify_overall_hmac(hmac_key, item):
verifier.update(key.encode('utf-8'))
verifier.update(value)
expected = base64.b64decode(item['hmac'])
got = verifier.digest()
if got != expected:
try:
verifier.verify(expected)
except InvalidSignature:
raise ValueError("HMAC did not match for data dictionary")
10 changes: 0 additions & 10 deletions onepassword/hashes.py

This file was deleted.

4 changes: 2 additions & 2 deletions onepassword/padding.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from . import random_util
import os

import six

Expand All @@ -24,7 +24,7 @@ def pkcs5_unpad(string):
return string[:-amount_of_padding]


def ab_pad(string, block_size=16, random_generator=random_util.sort_of_random_bytes):
def ab_pad(string, block_size=16, random_generator=os.urandom):
"""AgileBits custom pad a string to the given block size

Arguments:
Expand Down
14 changes: 4 additions & 10 deletions onepassword/pbkdf2.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,7 @@
pbkdf2_sha1 = pbkdf2_sha1
pbkdf2_sha512 = pbkdf2_sha512
except ImportError:
try:
from ._pbkdf2_cryptography import pbkdf2_sha1, pbkdf2_sha512
# make pyflakes happy
pbkdf2_sha1 = pbkdf2_sha1
pbkdf2_sha512 = pbkdf2_sha512
except ImportError:
from ._pbkdf2_pycrypto import pbkdf2_sha1, pbkdf2_sha512
# make pyflakes happy
pbkdf2_sha1 = pbkdf2_sha1
pbkdf2_sha512 = pbkdf2_sha512
from ._pbkdf2_cryptography import pbkdf2_sha1, pbkdf2_sha512
# make pyflakes happy
pbkdf2_sha1 = pbkdf2_sha1
pbkdf2_sha512 = pbkdf2_sha512
38 changes: 0 additions & 38 deletions onepassword/random_util.py

This file was deleted.

1 change: 0 additions & 1 deletion requirements-tests-py2.txt

This file was deleted.

1 change: 0 additions & 1 deletion requirements-tests-py3.txt

This file was deleted.

3 changes: 2 additions & 1 deletion requirements-tests.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
mock>=1.0
nose>=1.3
pytest>=2.7.2
pytest-benchmark>=2.5.0
1 change: 0 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
simplejson>=2.1.0
pycrypto>=2.0
cryptography>=0.9.3
six>=1.4.0
11 changes: 0 additions & 11 deletions tests/helpers.py

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
import os.path

from unittest2 import TestCase

import onepassword.keychain


class AgileKeychainIntegrationTestCase(TestCase):
class TestAgileKeychainIntegration:
test_file_root = os.path.realpath(os.path.join(__file__, '..', '..', '..', 'data', 'sample.agilekeychain'))

def test_open(self):
c = onepassword.keychain.AKeychain(self.test_file_root)
c.unlock("george")
self.assertEqual(len(c.items), 2)
assert len(c.items) == 2

def test_item_parsing(self):
c = onepassword.keychain.AKeychain(self.test_file_root)
c.unlock("george")
google = c.get_by_uuid('00925AACC28B482ABFE650FCD42F82CD')
self.assertEqual(google.title, 'Google')
self.assertEqual(google.decrypt()['fields'][1]['value'], 'test_password')
assert google.title == 'Google'
assert google.decrypt()['fields'][1]['value'] == 'test_password'
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import os.path

from unittest2 import TestCase
import pytest

import onepassword.keychain


class CloudKeychainIntegrationTestCase(TestCase):
class TestCloudKeychainIntegration:
test_file_root = os.path.realpath(os.path.join(__file__, '..', '..', '..', 'data', 'sample.cloudkeychain'))

def test_open(self):
Expand All @@ -16,5 +16,5 @@ def test_item_parsing(self):
c = onepassword.keychain.CKeychain(self.test_file_root)
c.unlock("fred")
skype_item = c.get_by_uuid('2A632FDD32F5445E91EB5636C7580447')
self.assertEqual(skype_item.title, 'Skype')
self.assertEqual(skype_item.decrypt()['fields'][1]['value'], 'dej3ur9unsh5ian1and5')
assert skype_item.title == 'Skype'
assert skype_item.decrypt()['fields'][1]['value'] == 'dej3ur9unsh5ian1and5'
44 changes: 0 additions & 44 deletions tests/unit/padding_tests.py

This file was deleted.

Loading