Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Port to Python 3 #2

Merged
merged 11 commits into from
May 15, 2017
Merged
4 changes: 1 addition & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,4 @@ local/
# Test data
datakey.dat
kek.dat
sample.cert
sample.key
source-cache.log
source-cache.log
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
language: python
python:
- 2.7
- 3.4
- 3.6
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why no 3.5?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can add it.
I didn't put it because I didn't have a python 3.5 environment on my machine, so I didn't test it myself. But travis is here for that!

install:
- python bootstrap.py
- bin/buildout
Expand Down
10 changes: 5 additions & 5 deletions README.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ This package provides a NIST SP 800-57 compliant Key Management Infrastructure

To get started do::

$ python bootstrap.py # Must be Python 2.5 or higher
$ ./bin/buildout # Depends on successfull compilation of M2Crypto
$ ./bin/runserver # or ./bin/paster serve server.ini
$ python bootstrap.py # Must be Python 2.7 or higher
$ ./bin/buildout # Depends on successful compilation of M2Crypto
$ ./bin/runserver # or ./bin/gunicorn --paste server.ini

The server will come up on port 8080. You can create a new key encrypting key
using::
Expand All @@ -15,7 +15,7 @@ using::

or, if you want a more convenient tool::

$ ./bin/testclient https://localhost:8080/new -n > kek.dat
$ ./bin/testclient https://localhost:8080 -n > kek.dat

The data encryption key can now be retrieved by posting the KEK to another
URL::
Expand All @@ -25,7 +25,7 @@ URL::

or ::

$ ./bin/testclient https://localhost:8080/new -g kek.dat > datakey.dat
$ ./bin/testclient https://localhost:8080 -g kek.dat > datakey.dat

Note: To be compliant, the server must use an encrypted communication channel
of course. The ``--ca-certificate`` tells wget to trust the sample self-signed
Expand Down
8 changes: 4 additions & 4 deletions buildout.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,17 @@ eggs = keas.kmi
[paster]
recipe = zc.recipe.egg
eggs = keas.kmi
PasteScript
gunicorn
pyOpenSSL
scripts = paster
scripts = gunicorn

[runserver]
recipe = zc.recipe.egg
eggs = ${paster:eggs}
scripts = paster=runserver
scripts = gunicorn=runserver
initialization =
import sys
sys.argv[1:] = ['serve', 'server.ini']
sys.argv[1:] = ['--paste', 'server.ini']

[testclient]
recipe = zc.recipe.egg
Expand Down
6 changes: 2 additions & 4 deletions generate-sample-cert.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
#!/bin/sh
openssl genrsa 1024 > sample.key
openssl req -new -x509 -nodes -sha1 -days 3650 -key sample.key > sample.cert
cat sample.cert sample.key > sample.pem
rm sample.key sample.cert

openssl req -new -x509 -nodes -sha1 -days 3650 -key sample.key > sample.crt
cat sample.crt sample.key > sample.pem
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it's time to replace -sha1 with -sha256?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW is the .pem file still needed? I see you're configuring it to use .key and .crt separately.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, sha256 definitely.
You're right, the .pem can be removed if we use "--ca-certificate sample.crt" instead of "--ca-certificate sample.pem" with wget. I can change the commands in the README.

16 changes: 16 additions & 0 deletions sample.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
-----BEGIN CERTIFICATE-----
MIICcTCCAdqgAwIBAgIJAMvA30EY1rKtMA0GCSqGSIb3DQEBBQUAMDAxCzAJBgNV
BAYTAlVTMQ0wCwYDVQQKEwRLZWFzMRIwEAYDVQQDEwlsb2NhbGhvc3QwHhcNMDgw
OTA0MTU1MTU5WhcNMTgwOTAyMTU1MTU5WjAwMQswCQYDVQQGEwJVUzENMAsGA1UE
ChMES2VhczESMBAGA1UEAxMJbG9jYWxob3N0MIGfMA0GCSqGSIb3DQEBAQUAA4GN
ADCBiQKBgQDK17rB/KVaK8MVjiEkvA4ZncOOIC3nStZ/erXM+qwkghPM4Tfr2FTU
iTgwwdLdu/ht74oWnppttfaTQ+sVz2rFXnPgfqKTGoJTwWFiuNuZhSRDVssGVnL/
RatZW6wns8UNf+W4hUe6/vGQP6obNTe2T4R+t2hXP51OkOy4BMcq0QIDAQABo4GS
MIGPMB0GA1UdDgQWBBQDIsX7HoSqbxKrCawi64MkXRmtmzBgBgNVHSMEWTBXgBQD
IsX7HoSqbxKrCawi64MkXRmtm6E0pDIwMDELMAkGA1UEBhMCVVMxDTALBgNVBAoT
BEtlYXMxEjAQBgNVBAMTCWxvY2FsaG9zdIIJAMvA30EY1rKtMAwGA1UdEwQFMAMB
Af8wDQYJKoZIhvcNAQEFBQADgYEAW5UBM7EIMpARzQwpQ8N1gyTR/VqJ9fSm4MIw
Y5m90HRgsDcXVbhn0rRfcC8o4EtGDvCjqsFYXy/ImF9tjEiuaysxbqepl+XMszPE
1kO50quWsV1FLSdcJX6t/ofJYOxiQkqPvg9t/ovTnEZ+w4NfPo+0MJgudjJoD2+w
5UTsKtU=
-----END CERTIFICATE-----
15 changes: 15 additions & 0 deletions sample.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQDK17rB/KVaK8MVjiEkvA4ZncOOIC3nStZ/erXM+qwkghPM4Tfr
2FTUiTgwwdLdu/ht74oWnppttfaTQ+sVz2rFXnPgfqKTGoJTwWFiuNuZhSRDVssG
VnL/RatZW6wns8UNf+W4hUe6/vGQP6obNTe2T4R+t2hXP51OkOy4BMcq0QIDAQAB
AoGAHcDJDx1M784NfoLrj6TZ+J3wik9kDFIo5mgMdLWsPGqsFthOSJTh1I8QI+66
THX++bkyKyE2i7MuKOnEeN2Ezo2jAThF7XoWhm6/+pSXhSqmL1jKr/1CZRaR9jv0
cCVJc3mTuAGH+yFVeGpWNvDaCmOUlD5M48xTROJXteDQ0TECQQDuDM9pmQdqkGIp
dvbIviS8donYn0kJ0TKS14pMtb/C63lcld513rHS43ru3FRY9baR/q5vV9vW5RhH
S7w4cYvVAkEA2iNLsFEAkY88oZJYbdyybeKxZdReyes1/zPe4RYzRdbDHRNAa+zk
mZIZDI820E0Y+DeoT+q3nXkXiiOS/iRNDQJBAKdAvOH2sO1AcJetjArS/cCkkIlw
sMKDB0OAyRzIfekXxPc2HU03oD0Jsy/sAh9W1GWTST/VvRIpeHtvTNljfdkCQF5T
UuBcNoW6zXoEYU6oV1Oi6hjhW1eu6PuAv4jPY754XoiNEZdZqYQqo8BFkWtDW1/C
GXrtQRbMDPzD40UYB2UCQQCmJpJp+u2lHj7zuZikHIHQBNyXyoGnzgNs6XUj1Bs6
Y4vjue8w6RkRLZ1YGP+xqsngVqb9IRygyLDpEgwEnOT4
-----END RSA PRIVATE KEY-----
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While this is convenient, if we don't ship a sample private key, people won't accidentally use it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The private key was already in the repo, the sample.pem contains both the public and private keys.
The sample certificate was committed I guess so you can test the app without having the openssl command. There is bin/testclient if you don't have wget too.

6 changes: 4 additions & 2 deletions server.ini
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ use = egg:keas.kmi
storage-dir=keys/

[server:main]
use = egg:Paste#http
use = egg:gunicorn#main
host = 0.0.0.0
port = 8080
ssl_pem = sample.pem
worker_class = sync
keyfile = sample.key
certfile = sample.crt

# Logging Configuration
[loggers]
Expand Down
60 changes: 29 additions & 31 deletions src/keas/kmi/README.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ infrastructure. Part of this infrastructure is a key management facility that
provides several services related to keys. All keys are stored in a specified
storage directory.

>>> from __future__ import print_function
>>> import tempfile
>>> storage_dir = tempfile.mkdtemp()

Expand Down Expand Up @@ -54,7 +55,7 @@ key:
Let's now use the key generation service's API to generate a key.

>>> key = keys.generate()
>>> print key
>>> print(key.decode())
-----BEGIN RSA PRIVATE KEY-----
...
-----END RSA PRIVATE KEY-----
Expand All @@ -65,10 +66,7 @@ HIPAA and NIST key strength requirement.

You can now use this key encrypting key to extract the encryption keys:

>>> try:
... from hashlib import md5
... except ImportError:
... from md5 import md5
>>> from hashlib import md5
>>> hash_key = md5(key).hexdigest()

>>> len(keys.get(hash_key))
Expand All @@ -82,34 +80,34 @@ you to encrypt and decrypt a string given the key encrypting key.

Let's now encrypt some data:

>>> encrypted = keys.encrypt(key, 'Stephan Richter')
>>> encrypted = keys.encrypt(key, b'Stephan Richter')
>>> len(encrypted)
16

We can also decrypt the data.

>>> keys.decrypt(key, encrypted)
'Stephan Richter'
>>> keys.decrypt(key, encrypted) == b'Stephan Richter'
True

We can also encrypt data given by a file descriptor

>>> import tempfile
>>> tmp_file = tempfile.TemporaryFile()
>>> data="encryptioniscool"*24*1024
>>> tmp_file.write(data)
>>> tmp_file.seek(0)
>>> data=b"encryptioniscool"*24*1024
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As long as you're changing this, can add the missing spaces around =?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure

>>> pos = tmp_file.write(data)
>>> pos = tmp_file.seek(0)
>>> encrypted_file = tempfile.TemporaryFile()
>>> keys.encrypt_file(key, tmp_file, encrypted_file)
>>> tmp_file.close()

And decrypt the file

>>> decrypted_file = tempfile.TemporaryFile()
>>> encrypted_file.seek(0)
>>> pos =encrypted_file.seek(0)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A space after the = would be nice.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oups, my bad.

>>> keys.decrypt_file(key, encrypted_file, decrypted_file)
>>> encrypted_file.close()

>>> decrypted_file.seek(0)
>>> pos = decrypted_file.seek(0)
>>> decrypted_data = decrypted_file.read()
>>> decrypted_file.close()
>>> decrypted_data == data
Expand Down Expand Up @@ -138,8 +136,8 @@ date/time the key has been fetched and the unencrypted DEK.

>>> firstTime = keys._KeyManagementFacility__dek_cache[hash_key][0]

>>> keys.decrypt(key, encrypted)
'Stephan Richter'
>>> keys.decrypt(key, encrypted) == b'Stephan Richter'
True

>>> secondTime = keys._KeyManagementFacility__dek_cache[hash_key][0]

Expand Down Expand Up @@ -186,12 +184,12 @@ As with the master facility, the local facility provides the

So en- and decryption is very easy to do:

>>> encrypted = localKeys.encrypt(key, 'Stephan Richter')
>>> encrypted = localKeys.encrypt(key, b'Stephan Richter')
>>> len(encrypted)
16

>>> localKeys.decrypt(key, encrypted)
'Stephan Richter'
>>> localKeys.decrypt(key, encrypted) == b'Stephan Richter'
True

Instead of forwarding the en- an decryption request to the master facility,
the local facility merely fetches the encryption key pair and executes the
Expand Down Expand Up @@ -225,8 +223,8 @@ decryption (private) key.

>>> firstTime = localKeys._LocalKeyManagementFacility__cache[key][0]

>>> localKeys.decrypt(key, encrypted)
'Stephan Richter'
>>> localKeys.decrypt(key, encrypted) == b'Stephan Richter'
True

>>> secondTime = localKeys._LocalKeyManagementFacility__cache[key][0]

Expand All @@ -241,7 +239,7 @@ The local facility also provides the ``IKeyGenerationService`` interface:
The local method call is identical to the master one:

>>> key2 = localKeys.generate()
>>> print key2
>>> print(key2.decode())
-----BEGIN RSA PRIVATE KEY-----
...
-----END RSA PRIVATE KEY-----
Expand Down Expand Up @@ -274,7 +272,7 @@ So let's have a look at the call:

>>> request = Request({})
>>> key3 = rest.create_key(keys, request).body
>>> print key3
>>> print(key3.decode())
-----BEGIN RSA PRIVATE KEY-----
...
-----END RSA PRIVATE KEY-----
Expand Down Expand Up @@ -302,13 +300,13 @@ encryption key string:
If you try to request a nonexistent key, you get a 404 error: encryption key
string:

>>> request.body = 'xxyz'
>>> print rest.get_key(keys, request)
>>> request.body = b'xxyz'
>>> print(rest.get_key(keys, request))
Key not found

A `GET` request to the root shows us a server status page

>>> print rest.get_status(keys, Request({}))
>>> print(rest.get_status(keys, Request({})))
200 OK
Content-Type: text/plain
Content-Length: 25
Expand All @@ -334,7 +332,7 @@ Of course, the key generation service is supported:
However, you will always receive the same key:

>>> def getKeySegment(key):
... return key.split('\n')[1]
... return str(key.decode().split('\n')[1])

>>> getKeySegment(testingKeys.generate())
'MIIBOAIBAAJBAL+VS9lDsS9XOaeJppfK9lhxKMRFdcg50MR3aJEQK9rvDEqNwBS9'
Expand All @@ -349,14 +347,14 @@ However, you will always receive the same key:
All other methods remain the same:

>>> key = testingKeys.generate()
>>> testingKeys.getEncryptionKey(key)
'_\xc4\x04\xbe5B\x7f\xaf\xd6\x92\xbd\xa0\xcf\x156\x1d\x88=p9{\xaa...'
>>> testingKeys.getEncryptionKey(key) == b'_\xc4\x04\xbe5B\x7f\xaf\xd6\x92\xbd\xa0\xcf\x156\x1d\x88=p9{\xaal\xb4\x84M\x1d\xfd\xb2z\xae\x1a'
True

We can also safely en- and decrypt:

>>> encrypted = testingKeys.encrypt(key, 'Stephan Richter')
>>> testingKeys.decrypt(key, encrypted)
'Stephan Richter'
>>> encrypted = testingKeys.encrypt(key, b'Stephan Richter')
>>> testingKeys.decrypt(key, encrypted) == b'Stephan Richter'
True


Key Holder
Expand Down