1
0
mirror of https://github.com/netbox-community/netbox.git synced 2024-05-10 07:54:54 +00:00

Implemented SessionKeys for secrets

This commit is contained in:
Jeremy Stretch
2017-02-03 12:49:32 -05:00
parent cf66f67fb6
commit a42eeb12d2
4 changed files with 164 additions and 54 deletions

View File

@ -1,5 +1,6 @@
import datetime
import os
from Crypto.Cipher import AES, PKCS1_OAEP
from Crypto.Cipher import AES, PKCS1_OAEP, XOR
from Crypto.PublicKey import RSA
from django.conf import settings
@ -8,6 +9,7 @@ from django.contrib.auth.models import Group, User
from django.core.exceptions import ValidationError
from django.core.urlresolvers import reverse
from django.db import models
from django.utils import timezone
from django.utils.encoding import force_bytes, python_2_unicode_compatible
from dcim.models import Device
@ -16,11 +18,13 @@ from utilities.models import CreatedUpdatedModel
from .hashers import SecretValidationHasher
def generate_master_key():
def generate_random_key(bits=256):
"""
Generate a new 256-bit (32 bytes) AES key to be used for symmetric encryption of secrets.
Generate a random encryption key. Sizes is given in bits and must be in increments of 32.
"""
return os.urandom(32)
if bits % 32:
raise Exception("Invalid key size ({}). Key sizes must be in increments of 32 bits.".format(bits))
return os.urandom(bits / 8)
def encrypt_master_key(master_key, public_key):
@ -41,6 +45,14 @@ def decrypt_master_key(master_key_cipher, private_key):
return cipher.decrypt(master_key_cipher)
def xor_keys(key_a, key_b):
"""
Return the binary XOR of two given keys.
"""
xor = XOR.new(key_a)
return xor.encrypt(key_b)
class UserKeyQuerySet(models.QuerySet):
def active(self):
@ -58,7 +70,7 @@ class UserKey(CreatedUpdatedModel):
copy of the master encryption key. The encrypted instance of the master key can be decrypted only with the user's
matching (private) decryption key.
"""
user = models.OneToOneField(User, related_name='user_key', verbose_name='User')
user = models.OneToOneField(User, related_name='user_key', editable=False)
public_key = models.TextField(verbose_name='RSA public key')
master_key_cipher = models.BinaryField(max_length=512, blank=True, null=True, editable=False)
@ -121,7 +133,7 @@ class UserKey(CreatedUpdatedModel):
# If no other active UserKeys exist, generate a new master key and use it to activate this UserKey.
if self.is_filled() and not self.is_active() and not UserKey.objects.active().count():
master_key = generate_master_key()
master_key = generate_random_key()
self.master_key_cipher = encrypt_master_key(master_key, self.public_key)
super(UserKey, self).save(*args, **kwargs)
@ -171,6 +183,58 @@ class UserKey(CreatedUpdatedModel):
self.save()
@python_2_unicode_compatible
class SessionKey(models.Model):
"""
A SessionKey stores a User's temporary key to be used for the encryption and decryption of secrets.
"""
user = models.OneToOneField(User, related_name='session_key', editable=False)
cipher = models.BinaryField(max_length=512, editable=False)
hash = models.CharField(max_length=128, editable=False)
created = models.DateTimeField(auto_now_add=True)
expiration_time = models.DateTimeField(blank=True, null=True, editable=False)
key = None
class Meta:
ordering = ['user__username']
def __str__(self):
return self.user.username
def save(self, master_key=None, *args, **kwargs):
if master_key is None:
raise Exception("The master key must be provided to save a session key.")
# Generate a random 256-bit session key if one is not already defined
if self.key is None:
self.key = generate_random_key()
# Generate SHA256 hash using Django's built-in password hashing mechanism
self.hash = make_password(self.key)
# Encrypt master key using the session key
self.cipher = xor_keys(self.key, master_key)
# Calculate expiration time
# TODO: Define a SESSION_KEY_MAX_AGE configuration setting
self.expiration_time = timezone.now() + datetime.timedelta(hours=12)
super(SessionKey, self).save(*args, **kwargs)
def get_master_key(self, session_key):
# Validate the provided session key
if not check_password(session_key, self.hash):
raise Exception("Invalid session key")
# Decrypt master key using provided session key
master_key = xor_keys(session_key, self.cipher)
return master_key
@python_2_unicode_compatible
class SecretRole(models.Model):
"""