<commit_before>import os
import base64
import hashlib

from django.conf import settings
from django.db import models
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.backends import default_backend

from keybar.utils.crypto import xor_strings


def pbkdf2(salt, password):
    return hashlib.pbkdf2_hmac(
        'sha256',
        salt,
        password,
        settings.KEYBAR_KDF_ITERATIONS)


class Entry(models.Model):

    title = models.CharField(max_length=256, blank=True, default='')
    description = models.TextField(blank=True)

    # Is max_length=256 sufficient?
    username = models.CharField(max_length=256)
    value = models.TextField()

    # Those parts ar for encrypting/decrypting the stored values
    # The values are stored as 16-byte-salt$key where `key`
    # can be used to obtain the actual encryption key.
    key = models.BinaryField()

    @classmethod
    def derive_encryption_key_spec(cls, password):
        """Get the real encryption key.

        Don't use the password directly but derive a encryption key
        dynamically based on the password and a stored key.

        This allows for password and encryption key to be changed
        independently, e.g in case of a security breach.

        :return: A string of ``16-byte-salt$key``.
        """
        fernet_key = Fernet.generate_key()
        salt = os.urandom(4)
        hashed_value = pbkdf2(salt, password)

        # Ordering of the xor-arguments is important, because of
        # very simplified xor-implementation.
        visible_key = xor_strings(fernet_key, hashed_value)

        return salt + b'$' + visible_key

    def get_encryption_key(self, password):
        salt, visible_key = bytes(self.key).split(b'$', 1)
        hashed_value = pbkdf2(salt, password)
<commit_msg>Make the salt 16-bytes strong<commit_after>import os
import base64
import hashlib

from django.conf import settings
from django.db import models
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.backends import default_backend

from keybar.utils.crypto import xor_strings


def pbkdf2(salt, password):
    return hashlib.pbkdf2_hmac(
        'sha256',
        salt,
        password,
        settings.KEYBAR_KDF_ITERATIONS)


class Entry(models.Model):

    title = models.CharField(max_length=256, blank=True, default='')
    description = models.TextField(blank=True)

    # Is max_length=256 sufficient?
    username = models.CharField(max_length=256)
    value = models.TextField()

    # Those parts ar for encrypting/decrypting the stored values
    # The values are stored as 16-byte-salt$key where `key`
    # can be used to obtain the actual encryption key.
    key = models.BinaryField()

    @classmethod
    def derive_encryption_key_spec(cls, password):
        """Get the real encryption key.

        Don't use the password directly but derive a encryption key
        dynamically based on the password and a stored key.

        This allows for password and encryption key to be changed
        independently, e.g in case of a security breach.

        :return: A string of ``16-byte-salt$key``.
        """
        fernet_key = Fernet.generate_key()
        salt = os.urandom(16)
        hashed_value = pbkdf2(salt, password)

        # Ordering of the xor-arguments is important, because of
        # very simplified xor-implementation.
        visible_key = xor_strings(fernet_key, hashed_value)

        return salt + b'$' + visible_key

    def get_encryption_key(self, password):
        salt, visible_key = bytes(self.key).split(b'$', 1)
        hashed_value = pbkdf2(salt, password)

{'fraction_non_alphanumeric': 0.07756160830090791, 'fraction_numerical': 0.014267185473411154, 'mean_word_length': 3.1822125813449023, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4862327', 'n_tokens_mistral': 1200, 'n_tokens_neox': 1120, 'n_words': 346}