Consolidating shared code from modules/nacl.py & runners/nacl.py to utils/nacl.py. Adding some new tests to test sealedbox & secretbox encryption & decryption.

This commit is contained in:
Gareth J. Greenaway 2018-04-06 13:06:48 -07:00
parent 17e5a568bc
commit 3fbf6c3647
No known key found for this signature in database
GPG Key ID: 10B62F8A7CAD7A41
5 changed files with 568 additions and 498 deletions

View File

@ -152,85 +152,15 @@ Optional small program to encrypt data without needing salt modules.
# Import Python libs
from __future__ import absolute_import, print_function, unicode_literals
import base64
import os
# Import Salt libs
from salt.ext import six
import salt.syspaths
import salt.utils.files
import salt.utils.platform
import salt.utils.stringutils
import salt.utils.versions
import salt.utils.win_functions
import salt.utils.win_dacl
REQ_ERROR = None
try:
import libnacl.secret
import libnacl.sealed
except (ImportError, OSError) as e:
REQ_ERROR = 'libnacl import error, perhaps missing python libnacl package or should update.'
import salt.utils.nacl
__virtualname__ = 'nacl'
def __virtual__():
return (REQ_ERROR is None, REQ_ERROR)
def _get_config(**kwargs):
'''
Return configuration
'''
config = {
'box_type': 'sealedbox',
'sk': None,
'sk_file': os.path.join(__opts__['pki_dir'], 'master', 'nacl'),
'pk': None,
'pk_file': os.path.join(__opts__['pki_dir'], 'master', 'nacl.pub'),
}
config_key = '{0}.config'.format(__virtualname__)
try:
config.update(__salt__['config.get'](config_key, {}))
except (NameError, KeyError) as e:
# likly using salt-run so fallback to __opts__
config.update(__opts__.get(config_key, {}))
# pylint: disable=C0201
for k in set(config.keys()) & set(kwargs.keys()):
config[k] = kwargs[k]
return config
def _get_sk(**kwargs):
'''
Return sk
'''
config = _get_config(**kwargs)
key = config['sk']
sk_file = config['sk_file']
if not key and sk_file:
with salt.utils.files.fopen(sk_file, 'rb') as keyf:
key = salt.utils.stringutils.to_unicode(keyf.read()).rstrip('\n')
if key is None:
raise Exception('no key or sk_file found')
return base64.b64decode(key)
def _get_pk(**kwargs):
'''
Return pk
'''
config = _get_config(**kwargs)
pubkey = config['pk']
pk_file = config['pk_file']
if not pubkey and pk_file:
with salt.utils.files.fopen(pk_file, 'rb') as keyf:
pubkey = salt.utils.stringutils.to_unicode(keyf.read()).rstrip('\n')
if pubkey is None:
raise Exception('no pubkey or pk_file found')
pubkey = six.text_type(pubkey)
return base64.b64decode(pubkey)
return salt.utils.nacl.check_requirements()
def keygen(sk_file=None, pk_file=None, **kwargs):
@ -253,66 +183,8 @@ def keygen(sk_file=None, pk_file=None, **kwargs):
salt-call nacl.keygen sk_file=/etc/salt/pki/master/nacl pk_file=/etc/salt/pki/master/nacl.pub
salt-call --local nacl.keygen
'''
if 'keyfile' in kwargs:
salt.utils.versions.warn_until(
'Neon',
'The \'keyfile\' argument has been deprecated and will be removed in Salt '
'{version}. Please use \'sk_file\' argument instead.'
)
sk_file = kwargs['keyfile']
if sk_file is None:
kp = libnacl.public.SecretKey()
return {'sk': base64.b64encode(kp.sk), 'pk': base64.b64encode(kp.pk)}
if pk_file is None:
pk_file = '{0}.pub'.format(sk_file)
if sk_file and pk_file is None:
if not os.path.isfile(sk_file):
kp = libnacl.public.SecretKey()
with salt.utils.files.fopen(sk_file, 'wb') as keyf:
keyf.write(base64.b64encode(kp.sk))
if salt.utils.platform.is_windows():
cur_user = salt.utils.win_functions.get_current_user()
salt.utils.win_dacl.set_owner(sk_file, cur_user)
salt.utils.win_dacl.set_permissions(sk_file, cur_user, 'full_control', 'grant', reset_perms=True, protected=True)
else:
# chmod 0600 file
os.chmod(sk_file, 1536)
return 'saved sk_file: {0}'.format(sk_file)
else:
raise Exception('sk_file:{0} already exist.'.format(sk_file))
if sk_file is None and pk_file:
raise Exception('sk_file: Must be set inorder to generate a public key.')
if os.path.isfile(sk_file) and os.path.isfile(pk_file):
raise Exception('sk_file:{0} and pk_file:{1} already exist.'.format(sk_file, pk_file))
if os.path.isfile(sk_file) and not os.path.isfile(pk_file):
# generate pk using the sk
with salt.utils.files.fopen(sk_file, 'rb') as keyf:
sk = salt.utils.stringutils.to_unicode(keyf.read()).rstrip('\n')
sk = base64.b64decode(sk)
kp = libnacl.public.SecretKey(sk)
with salt.utils.files.fopen(pk_file, 'wb') as keyf:
keyf.write(base64.b64encode(kp.pk))
return 'saved pk_file: {0}'.format(pk_file)
kp = libnacl.public.SecretKey()
with salt.utils.files.fopen(sk_file, 'wb') as keyf:
keyf.write(base64.b64encode(kp.sk))
if salt.utils.platform.is_windows():
cur_user = salt.utils.win_functions.get_current_user()
salt.utils.win_dacl.set_owner(sk_file, cur_user)
salt.utils.win_dacl.set_permissions(sk_file, cur_user, 'full_control', 'grant', reset_perms=True, protected=True)
else:
# chmod 0600 file
os.chmod(sk_file, 1536)
with salt.utils.files.fopen(pk_file, 'wb') as keyf:
keyf.write(base64.b64encode(kp.pk))
return 'saved sk_file:{0} pk_file: {1}'.format(sk_file, pk_file)
kwargs['opts'] = __opts__
return salt.utils.nacl.keygen(sk_file, pk_file, **kwargs)
def enc(data, **kwargs):
@ -321,31 +193,8 @@ def enc(data, **kwargs):
box_type: secretbox, sealedbox(default)
'''
if 'keyfile' in kwargs:
salt.utils.versions.warn_until(
'Neon',
'The \'keyfile\' argument has been deprecated and will be removed in Salt '
'{version}. Please use \'sk_file\' argument instead.'
)
kwargs['sk_file'] = kwargs['keyfile']
if 'key' in kwargs:
salt.utils.versions.warn_until(
'Neon',
'The \'key\' argument has been deprecated and will be removed in Salt '
'{version}. Please use \'sk\' argument instead.'
)
kwargs['sk'] = kwargs['key']
# ensure data is in bytes
data = salt.utils.stringutils.to_bytes(data)
box_type = _get_config(**kwargs)['box_type']
if box_type == 'sealedbox':
return sealedbox_encrypt(data, **kwargs)
if box_type == 'secretbox':
return secretbox_encrypt(data, **kwargs)
return sealedbox_encrypt(data, **kwargs)
kwargs['opts'] = __opts__
return salt.utils.nacl.enc(data, **kwargs)
def enc_file(name, out=None, **kwargs):
@ -365,20 +214,8 @@ def enc_file(name, out=None, **kwargs):
salt-run nacl.enc_file name=/tmp/id_rsa box_type=secretbox \
sk_file=/etc/salt/pki/master/nacl.pub
'''
try:
data = __salt__['cp.get_file_str'](name)
except Exception as e:
# likly using salt-run so fallback to local filesystem
with salt.utils.files.fopen(name, 'rb') as f:
data = salt.utils.stringutils.to_unicode(f.read())
d = enc(data, **kwargs)
if out:
if os.path.isfile(out):
raise Exception('file:{0} already exist.'.format(out))
with salt.utils.files.fopen(out, 'wb') as f:
f.write(salt.utils.stringutils.to_bytes(d))
return 'Wrote: {0}'.format(out)
return d
kwargs['opts'] = __opts__
return salt.utils.nacl.enc_file(name, out, **kwargs)
def dec(data, **kwargs):
@ -387,37 +224,8 @@ def dec(data, **kwargs):
box_type: secretbox, sealedbox(default)
'''
if 'keyfile' in kwargs:
salt.utils.versions.warn_until(
'Neon',
'The \'keyfile\' argument has been deprecated and will be removed in Salt '
'{version}. Please use \'sk_file\' argument instead.'
)
kwargs['sk_file'] = kwargs['keyfile']
# set boxtype to `secretbox` to maintain backward compatibility
kwargs['box_type'] = 'secretbox'
if 'key' in kwargs:
salt.utils.versions.warn_until(
'Neon',
'The \'key\' argument has been deprecated and will be removed in Salt '
'{version}. Please use \'sk\' argument instead.'
)
kwargs['sk'] = kwargs['key']
# set boxtype to `secretbox` to maintain backward compatibility
kwargs['box_type'] = 'secretbox'
# ensure data is in bytes
data = salt.utils.stringutils.to_bytes(data)
box_type = _get_config(**kwargs)['box_type']
if box_type == 'sealedbox':
return sealedbox_decrypt(data, **kwargs)
if box_type == 'secretbox':
return secretbox_decrypt(data, **kwargs)
return sealedbox_decrypt(data, **kwargs)
kwargs['opts'] = __opts__
return salt.utils.nacl.dec(data, **kwargs)
def dec_file(name, out=None, **kwargs):
@ -437,20 +245,8 @@ def dec_file(name, out=None, **kwargs):
salt-run nacl.dec_file name=/tmp/id_rsa.nacl box_type=secretbox \
sk_file=/etc/salt/pki/master/nacl.pub
'''
try:
data = __salt__['cp.get_file_str'](name)
except Exception as e:
# likly using salt-run so fallback to local filesystem
with salt.utils.files.fopen(name, 'rb') as f:
data = salt.utils.stringutils.to_unicode(f.read())
d = dec(data, **kwargs)
if out:
if os.path.isfile(out):
raise Exception('file:{0} already exist.'.format(out))
with salt.utils.files.fopen(out, 'wb') as f:
f.write(salt.utils.stringutils.to_bytes(d))
return 'Wrote: {0}'.format(out)
return d
kwargs['opts'] = __opts__
return salt.utils.nacl.dec_file(name, out, **kwargs)
def sealedbox_encrypt(data, **kwargs):
@ -466,12 +262,8 @@ def sealedbox_encrypt(data, **kwargs):
salt-call --local nacl.sealedbox_encrypt datatoenc pk_file=/etc/salt/pki/master/nacl.pub
salt-call --local nacl.sealedbox_encrypt datatoenc pk='vrwQF7cNiNAVQVAiS3bvcbJUnF0cN6fU9YTZD9mBfzQ='
'''
# ensure data is in bytes
data = salt.utils.stringutils.to_bytes(data)
pk = _get_pk(**kwargs)
b = libnacl.sealed.SealedBox(pk)
return base64.b64encode(b.encrypt(data))
kwargs['opts'] = __opts__
return salt.utils.nacl.sealedbox_encrypt(data, **kwargs)
def sealedbox_decrypt(data, **kwargs):
@ -486,16 +278,8 @@ def sealedbox_decrypt(data, **kwargs):
salt-call --local nacl.sealedbox_decrypt data='pEXHQM6cuaF7A=' sk_file=/etc/salt/pki/master/nacl
salt-call --local nacl.sealedbox_decrypt data='pEXHQM6cuaF7A=' sk='YmFkcGFzcwo='
'''
if data is None:
return None
# ensure data is in bytes
data = salt.utils.stringutils.to_bytes(data)
sk = _get_sk(**kwargs)
keypair = libnacl.public.SecretKey(sk)
b = libnacl.sealed.SealedBox(keypair)
return b.decrypt(base64.b64decode(data))
kwargs['opts'] = __opts__
return salt.utils.nacl.sealedbox_decrypt(data, **kwargs)
def secretbox_encrypt(data, **kwargs):
@ -511,12 +295,8 @@ def secretbox_encrypt(data, **kwargs):
salt-call --local nacl.secretbox_encrypt datatoenc sk_file=/etc/salt/pki/master/nacl
salt-call --local nacl.secretbox_encrypt datatoenc sk='YmFkcGFzcwo='
'''
# ensure data is in bytes
data = salt.utils.stringutils.to_bytes(data)
sk = _get_sk(**kwargs)
b = libnacl.secret.SecretBox(sk)
return base64.b64encode(b.encrypt(data))
kwargs['opts'] = __opts__
return salt.utils.nacl.secretbox_encrypt(data, **kwargs)
def secretbox_decrypt(data, **kwargs):
@ -532,12 +312,5 @@ def secretbox_decrypt(data, **kwargs):
salt-call --local nacl.secretbox_decrypt data='pEXHQM6cuaF7A=' sk_file=/etc/salt/pki/master/nacl
salt-call --local nacl.secretbox_decrypt data='pEXHQM6cuaF7A=' sk='YmFkcGFzcwo='
'''
if data is None:
return None
# ensure data is in bytes
data = salt.utils.stringutils.to_bytes(data)
key = _get_sk(**kwargs)
b = libnacl.secret.SecretBox(key=key)
return b.decrypt(base64.b64decode(data))
kwargs['opts'] = __opts__
return salt.utils.nacl.secretbox_decrypt(data, **kwargs)

View File

@ -114,87 +114,15 @@ Larger files like certificates can be encrypted with:
# Import Python libs
from __future__ import absolute_import, print_function, unicode_literals
import base64
import os
# Import Salt libs
import salt.utils.files
import salt.utils.platform
import salt.utils.stringutils
import salt.utils.versions
import salt.utils.win_functions
import salt.utils.win_dacl
import salt.syspaths
# Import 3rd-party libs
from salt.ext import six
REQ_ERROR = None
try:
import libnacl.secret
import libnacl.sealed
except (ImportError, OSError) as e:
REQ_ERROR = 'libnacl import error, perhaps missing python libnacl package or should update.'
import salt.utils.nacl
__virtualname__ = 'nacl'
def __virtual__():
return (REQ_ERROR is None, REQ_ERROR)
def _get_config(**kwargs):
'''
Return configuration
'''
config = {
'box_type': 'sealedbox',
'sk': None,
'sk_file': os.path.join(__opts__['pki_dir'], 'nacl'),
'pk': None,
'pk_file': os.path.join(__opts__['pki_dir'], 'nacl.pub'),
}
config_key = '{0}.config'.format(__virtualname__)
try:
config.update(__salt__['config.get'](config_key, {}))
except (NameError, KeyError) as e:
# likly using salt-run so fallback to __opts__
config.update(__opts__.get(config_key, {}))
# pylint: disable=C0201
for k in set(config.keys()) & set(kwargs.keys()):
config[k] = kwargs[k]
return config
def _get_sk(**kwargs):
'''
Return sk
'''
config = _get_config(**kwargs)
key = config['sk']
sk_file = config['sk_file']
if not key and sk_file:
with salt.utils.files.fopen(sk_file, 'rb') as keyf:
key = six.text_type(keyf.read()).rstrip('\n')
if key is None:
raise Exception('no key or sk_file found')
return base64.b64decode(key)
def _get_pk(**kwargs):
'''
Return pk
'''
config = _get_config(**kwargs)
pubkey = config['pk']
pk_file = config['pk_file']
if not pubkey and pk_file:
with salt.utils.files.fopen(pk_file, 'rb') as keyf:
pubkey = six.text_type(keyf.read()).rstrip('\n')
if pubkey is None:
raise Exception('no pubkey or pk_file found')
pubkey = six.text_type(pubkey)
return base64.b64decode(pubkey)
return salt.utils.nacl.check_requirements()
def keygen(sk_file=None, pk_file=None, **kwargs):
@ -217,67 +145,8 @@ def keygen(sk_file=None, pk_file=None, **kwargs):
salt-run nacl.keygen sk_file=/etc/salt/pki/master/nacl pk_file=/etc/salt/pki/master/nacl.pub
salt-run nacl.keygen
'''
if 'keyfile' in kwargs:
salt.utils.versions.warn_until(
'Neon',
'The \'keyfile\' argument has been deprecated and will be removed in Salt '
'{version}. Please use \'sk_file\' argument instead.'
)
sk_file = kwargs['keyfile']
if sk_file is None:
kp = libnacl.public.SecretKey()
return {'sk': base64.b64encode(kp.sk), 'pk': base64.b64encode(kp.pk)}
if pk_file is None:
pk_file = '{0}.pub'.format(sk_file)
if sk_file and pk_file is None:
if not os.path.isfile(sk_file):
kp = libnacl.public.SecretKey()
with salt.utils.files.fopen(sk_file, 'w') as keyf:
keyf.write(base64.b64encode(kp.sk))
if salt.utils.platform.is_windows():
cur_user = salt.utils.win_functions.get_current_user()
salt.utils.win_dacl.set_owner(sk_file, cur_user)
salt.utils.win_dacl.set_permissions(sk_file, cur_user, 'full_control', 'grant', reset_perms=True, protected=True)
else:
# chmod 0600 file
os.chmod(sk_file, 1536)
return 'saved sk_file: {0}'.format(sk_file)
else:
raise Exception('sk_file:{0} already exist.'.format(sk_file))
if sk_file is None and pk_file:
raise Exception('sk_file: Must be set inorder to generate a public key.')
if os.path.isfile(sk_file) and os.path.isfile(pk_file):
raise Exception('sk_file:{0} and pk_file:{1} already exist.'.format(sk_file, pk_file))
if os.path.isfile(sk_file) and not os.path.isfile(pk_file):
# generate pk using the sk
with salt.utils.files.fopen(sk_file, 'rb') as keyf:
sk = six.text_type(keyf.read()).rstrip('\n')
sk = base64.b64decode(sk)
kp = libnacl.public.SecretKey(sk)
with salt.utils.files.fopen(pk_file, 'w') as keyf:
keyf.write(base64.b64encode(kp.pk))
return 'saved pk_file: {0}'.format(pk_file)
kp = libnacl.public.SecretKey()
with salt.utils.files.fopen(sk_file, 'w') as keyf:
keyf.write(base64.b64encode(kp.sk))
if salt.utils.platform.is_windows():
cur_user = salt.utils.win_functions.get_current_user()
salt.utils.win_dacl.set_owner(sk_file, cur_user)
salt.utils.win_dacl.set_permissions(sk_file, cur_user, 'full_control', 'grant', reset_perms=True, protected=True)
else:
# chmod 0600 file
os.chmod(sk_file, 1536)
with salt.utils.files.fopen(pk_file, 'w') as keyf:
keyf.write(base64.b64encode(kp.pk))
return 'saved sk_file:{0} pk_file: {1}'.format(sk_file, pk_file)
kwargs['opts'] = __opts__
return salt.utils.nacl.keygen(sk_file, pk_file, **kwargs)
def enc(data, **kwargs):
@ -286,32 +155,8 @@ def enc(data, **kwargs):
box_type: secretbox, sealedbox(default)
'''
if 'keyfile' in kwargs:
salt.utils.versions.warn_until(
'Neon',
'The \'keyfile\' argument has been deprecated and will be removed in Salt '
'{version}. Please use \'sk_file\' argument instead.'
)
kwargs['sk_file'] = kwargs['keyfile']
if 'key' in kwargs:
salt.utils.versions.warn_until(
'Neon',
'The \'key\' argument has been deprecated and will be removed in Salt '
'{version}. Please use \'sk\' argument instead.'
)
kwargs['sk'] = kwargs['key']
# ensure data is bytes
data = salt.utils.stringutils.to_bytes(data)
box_type = _get_config(**kwargs)['box_type']
if box_type == 'sealedbox':
return sealedbox_encrypt(data, **kwargs)
if box_type == 'secretbox':
return secretbox_encrypt(data, **kwargs)
return sealedbox_encrypt(data, **kwargs)
kwargs['opts'] = __opts__
return salt.utils.nacl.enc(data, **kwargs)
def enc_file(name, out=None, **kwargs):
@ -330,20 +175,8 @@ def enc_file(name, out=None, **kwargs):
salt-run nacl.enc_file name=/tmp/id_rsa box_type=secretbox \
sk_file=/etc/salt/pki/master/nacl.pub
'''
try:
data = __salt__['cp.get_file_str'](name)
except Exception as e:
# likly using salt-run so fallback to local filesystem
with salt.utils.files.fopen(name, 'rb') as f:
data = f.read()
d = enc(data, **kwargs)
if out:
if os.path.isfile(out):
raise Exception('file:{0} already exist.'.format(out))
with salt.utils.files.fopen(out, 'wb') as f:
f.write(d)
return 'Wrote: {0}'.format(out)
return d
kwargs['opts'] = __opts__
return salt.utils.nacl.enc_file(name, out, **kwargs)
def dec(data, **kwargs):
@ -352,37 +185,8 @@ def dec(data, **kwargs):
box_type: secretbox, sealedbox(default)
'''
if 'keyfile' in kwargs:
salt.utils.versions.warn_until(
'Neon',
'The \'keyfile\' argument has been deprecated and will be removed in Salt '
'{version}. Please use \'sk_file\' argument instead.'
)
kwargs['sk_file'] = kwargs['keyfile']
# set boxtype to `secretbox` to maintain backward compatibility
kwargs['box_type'] = 'secretbox'
if 'key' in kwargs:
salt.utils.versions.warn_until(
'Neon',
'The \'key\' argument has been deprecated and will be removed in Salt '
'{version}. Please use \'sk\' argument instead.'
)
kwargs['sk'] = kwargs['key']
# set boxtype to `secretbox` to maintain backward compatibility
kwargs['box_type'] = 'secretbox'
# ensure data is bytes
data = salt.utils.stringutils.to_bytes(data)
box_type = _get_config(**kwargs)['box_type']
if box_type == 'sealedbox':
return sealedbox_decrypt(data, **kwargs)
if box_type == 'secretbox':
return secretbox_decrypt(data, **kwargs)
return sealedbox_decrypt(data, **kwargs)
kwargs['opts'] = __opts__
return salt.utils.nacl.dec(data, **kwargs)
def dec_file(name, out=None, **kwargs):
@ -401,20 +205,8 @@ def dec_file(name, out=None, **kwargs):
salt-run nacl.dec_file name=/tmp/id_rsa.nacl box_type=secretbox \
sk_file=/etc/salt/pki/master/nacl.pub
'''
try:
data = __salt__['cp.get_file_str'](name)
except Exception as e:
# likly using salt-run so fallback to local filesystem
with salt.utils.files.fopen(name, 'rb') as f:
data = f.read()
d = dec(data, **kwargs)
if out:
if os.path.isfile(out):
raise Exception('file:{0} already exist.'.format(out))
with salt.utils.files.fopen(out, 'wb') as f:
f.write(d)
return 'Wrote: {0}'.format(out)
return d
kwargs['opts'] = __opts__
return salt.utils.nacl.dec_file(name, out, **kwargs)
def sealedbox_encrypt(data, **kwargs):
@ -428,12 +220,8 @@ def sealedbox_encrypt(data, **kwargs):
salt-run nacl.sealedbox_encrypt datatoenc
'''
# ensure data is bytes
data = salt.utils.stringutils.to_bytes(data)
pk = _get_pk(**kwargs)
b = libnacl.sealed.SealedBox(pk)
return base64.b64encode(b.encrypt(data))
kwargs['opts'] = __opts__
return salt.utils.nacl.sealedbox_encrypt(data, **kwargs)
def sealedbox_decrypt(data, **kwargs):
@ -448,16 +236,8 @@ def sealedbox_decrypt(data, **kwargs):
salt-run nacl.sealedbox_decrypt data='pEXHQM6cuaF7A=' sk_file=/etc/salt/pki/master/nacl
salt-run nacl.sealedbox_decrypt data='pEXHQM6cuaF7A=' sk='YmFkcGFzcwo='
'''
if data is None:
return None
# ensure data is bytes
data = salt.utils.stringutils.to_bytes(data)
sk = _get_sk(**kwargs)
keypair = libnacl.public.SecretKey(sk)
b = libnacl.sealed.SealedBox(keypair)
return b.decrypt(base64.b64decode(data))
kwargs['opts'] = __opts__
return salt.utils.nacl.sealedbox_decrypt(data, **kwargs)
def secretbox_encrypt(data, **kwargs):
@ -473,12 +253,8 @@ def secretbox_encrypt(data, **kwargs):
salt-run nacl.secretbox_encrypt datatoenc sk_file=/etc/salt/pki/master/nacl
salt-run nacl.secretbox_encrypt datatoenc sk='YmFkcGFzcwo='
'''
# ensure data is bytes
data = salt.utils.stringutils.to_bytes(data)
sk = _get_sk(**kwargs)
b = libnacl.secret.SecretBox(sk)
return base64.b64encode(b.encrypt(data))
kwargs['opts'] = __opts__
return salt.utils.nacl.secretbox_encrypt(data, **kwargs)
def secretbox_decrypt(data, **kwargs):
@ -494,12 +270,5 @@ def secretbox_decrypt(data, **kwargs):
salt-run nacl.secretbox_decrypt data='pEXHQM6cuaF7A=' sk_file=/etc/salt/pki/master/nacl
salt-run nacl.secretbox_decrypt data='pEXHQM6cuaF7A=' sk='YmFkcGFzcwo='
'''
if data is None:
return None
# ensure data is bytes
data = salt.utils.stringutils.to_bytes(data)
key = _get_sk(**kwargs)
b = libnacl.secret.SecretBox(key=key)
return b.decrypt(base64.b64decode(data))
kwargs['opts'] = __opts__
return salt.utils.nacl.secretbox_decrypt(data, **kwargs)

404
salt/utils/nacl.py Normal file
View File

@ -0,0 +1,404 @@
# -*- coding: utf-8 -*-
'''
Common code shared between the nacl module and runner.
'''
# Import Python libs
from __future__ import absolute_import, print_function, unicode_literals
import base64
import os
# Import Salt libs
from salt.ext import six
import salt.syspaths
import salt.utils.files
import salt.utils.platform
import salt.utils.stringutils
import salt.utils.versions
import salt.utils.win_functions
import salt.utils.win_dacl
REQ_ERROR = None
try:
import libnacl.secret
import libnacl.sealed
except (ImportError, OSError) as e:
REQ_ERROR = 'libnacl import error, perhaps missing python libnacl package or should update.'
__virtualname__ = 'nacl'
def __virtual__():
return check_requirements()
def check_requirements():
'''
Check required libraries are available
'''
return (REQ_ERROR is None, REQ_ERROR)
def _get_config(**kwargs):
'''
Return configuration
'''
config = {
'box_type': 'sealedbox',
'sk': None,
'sk_file': os.path.join(kwargs['opts'].get('pki_dir'), 'master/nacl'),
'pk': None,
'pk_file': os.path.join(kwargs['opts'].get('pki_dir'), 'master/nacl.pub'),
}
config_key = '{0}.config'.format(__virtualname__)
try:
config.update(__salt__['config.get'](config_key, {}))
except (NameError, KeyError) as e:
# likly using salt-run so fallback to __opts__
config.update(kwargs['opts'].get(config_key, {}))
# pylint: disable=C0201
for k in set(config.keys()) & set(kwargs.keys()):
config[k] = kwargs[k]
return config
def _get_sk(**kwargs):
'''
Return sk
'''
config = _get_config(**kwargs)
key = config['sk']
sk_file = config['sk_file']
if not key and sk_file:
with salt.utils.files.fopen(sk_file, 'rb') as keyf:
key = salt.utils.stringutils.to_unicode(keyf.read()).rstrip('\n')
if key is None:
raise Exception('no key or sk_file found')
return base64.b64decode(key)
def _get_pk(**kwargs):
'''
Return pk
'''
config = _get_config(**kwargs)
pubkey = config['pk']
pk_file = config['pk_file']
if not pubkey and pk_file:
with salt.utils.files.fopen(pk_file, 'rb') as keyf:
pubkey = salt.utils.stringutils.to_unicode(keyf.read()).rstrip('\n')
if pubkey is None:
raise Exception('no pubkey or pk_file found')
pubkey = six.text_type(pubkey)
return base64.b64decode(pubkey)
def keygen(sk_file=None, pk_file=None, **kwargs):
'''
Use libnacl to generate a keypair.
If no `sk_file` is defined return a keypair.
If only the `sk_file` is defined `pk_file` will use the same name with a postfix `.pub`.
When the `sk_file` is already existing, but `pk_file` is not. The `pk_file` will be generated
using the `sk_file`.
CLI Examples:
.. code-block:: bash
salt-call nacl.keygen
salt-call nacl.keygen sk_file=/etc/salt/pki/master/nacl
salt-call nacl.keygen sk_file=/etc/salt/pki/master/nacl pk_file=/etc/salt/pki/master/nacl.pub
salt-call --local nacl.keygen
'''
if 'keyfile' in kwargs:
salt.utils.versions.warn_until(
'Neon',
'The \'keyfile\' argument has been deprecated and will be removed in Salt '
'{version}. Please use \'sk_file\' argument instead.'
)
sk_file = kwargs['keyfile']
if sk_file is None:
kp = libnacl.public.SecretKey()
return {'sk': base64.b64encode(kp.sk), 'pk': base64.b64encode(kp.pk)}
if pk_file is None:
pk_file = '{0}.pub'.format(sk_file)
if sk_file and pk_file is None:
if not os.path.isfile(sk_file):
kp = libnacl.public.SecretKey()
with salt.utils.files.fopen(sk_file, 'wb') as keyf:
keyf.write(base64.b64encode(kp.sk))
if salt.utils.platform.is_windows():
cur_user = salt.utils.win_functions.get_current_user()
salt.utils.win_dacl.set_owner(sk_file, cur_user)
salt.utils.win_dacl.set_permissions(sk_file, cur_user, 'full_control', 'grant', reset_perms=True, protected=True)
else:
# chmod 0600 file
os.chmod(sk_file, 1536)
return 'saved sk_file: {0}'.format(sk_file)
else:
raise Exception('sk_file:{0} already exist.'.format(sk_file))
if sk_file is None and pk_file:
raise Exception('sk_file: Must be set inorder to generate a public key.')
if os.path.isfile(sk_file) and os.path.isfile(pk_file):
raise Exception('sk_file:{0} and pk_file:{1} already exist.'.format(sk_file, pk_file))
if os.path.isfile(sk_file) and not os.path.isfile(pk_file):
# generate pk using the sk
with salt.utils.files.fopen(sk_file, 'rb') as keyf:
sk = salt.utils.stringutils.to_unicode(keyf.read()).rstrip('\n')
sk = base64.b64decode(sk)
kp = libnacl.public.SecretKey(sk)
with salt.utils.files.fopen(pk_file, 'wb') as keyf:
keyf.write(base64.b64encode(kp.pk))
return 'saved pk_file: {0}'.format(pk_file)
kp = libnacl.public.SecretKey()
with salt.utils.files.fopen(sk_file, 'wb') as keyf:
keyf.write(base64.b64encode(kp.sk))
if salt.utils.platform.is_windows():
cur_user = salt.utils.win_functions.get_current_user()
salt.utils.win_dacl.set_owner(sk_file, cur_user)
salt.utils.win_dacl.set_permissions(sk_file, cur_user, 'full_control', 'grant', reset_perms=True, protected=True)
else:
# chmod 0600 file
os.chmod(sk_file, 1536)
with salt.utils.files.fopen(pk_file, 'wb') as keyf:
keyf.write(base64.b64encode(kp.pk))
return 'saved sk_file:{0} pk_file: {1}'.format(sk_file, pk_file)
def enc(data, **kwargs):
'''
Alias to `{box_type}_encrypt`
box_type: secretbox, sealedbox(default)
'''
if 'keyfile' in kwargs:
salt.utils.versions.warn_until(
'Neon',
'The \'keyfile\' argument has been deprecated and will be removed in Salt '
'{version}. Please use \'sk_file\' argument instead.'
)
kwargs['sk_file'] = kwargs['keyfile']
if 'key' in kwargs:
salt.utils.versions.warn_until(
'Neon',
'The \'key\' argument has been deprecated and will be removed in Salt '
'{version}. Please use \'sk\' argument instead.'
)
kwargs['sk'] = kwargs['key']
# ensure data is in bytes
data = salt.utils.stringutils.to_bytes(data)
box_type = _get_config(**kwargs)['box_type']
if box_type == 'sealedbox':
return sealedbox_encrypt(data, **kwargs)
if box_type == 'secretbox':
return secretbox_encrypt(data, **kwargs)
return sealedbox_encrypt(data, **kwargs)
def enc_file(name, out=None, **kwargs):
'''
This is a helper function to encrypt a file and return its contents.
You can provide an optional output file using `out`
`name` can be a local file or when not using `salt-run` can be a url like `salt://`, `https://` etc.
CLI Examples:
.. code-block:: bash
salt-run nacl.enc_file name=/tmp/id_rsa
salt-call nacl.enc_file name=salt://crt/mycert out=/tmp/cert
salt-run nacl.enc_file name=/tmp/id_rsa box_type=secretbox \
sk_file=/etc/salt/pki/master/nacl.pub
'''
try:
data = __salt__['cp.get_file_str'](name)
except Exception as e:
# likly using salt-run so fallback to local filesystem
with salt.utils.files.fopen(name, 'rb') as f:
data = salt.utils.stringutils.to_unicode(f.read())
d = enc(data, **kwargs)
if out:
if os.path.isfile(out):
raise Exception('file:{0} already exist.'.format(out))
with salt.utils.files.fopen(out, 'wb') as f:
f.write(salt.utils.stringutils.to_bytes(d))
return 'Wrote: {0}'.format(out)
return d
def dec(data, **kwargs):
'''
Alias to `{box_type}_decrypt`
box_type: secretbox, sealedbox(default)
'''
if 'keyfile' in kwargs:
salt.utils.versions.warn_until(
'Neon',
'The \'keyfile\' argument has been deprecated and will be removed in Salt '
'{version}. Please use \'sk_file\' argument instead.'
)
kwargs['sk_file'] = kwargs['keyfile']
# set boxtype to `secretbox` to maintain backward compatibility
kwargs['box_type'] = 'secretbox'
if 'key' in kwargs:
salt.utils.versions.warn_until(
'Neon',
'The \'key\' argument has been deprecated and will be removed in Salt '
'{version}. Please use \'sk\' argument instead.'
)
kwargs['sk'] = kwargs['key']
# set boxtype to `secretbox` to maintain backward compatibility
kwargs['box_type'] = 'secretbox'
# ensure data is in bytes
data = salt.utils.stringutils.to_bytes(data)
box_type = _get_config(**kwargs)['box_type']
if box_type == 'sealedbox':
return sealedbox_decrypt(data, **kwargs)
if box_type == 'secretbox':
return secretbox_decrypt(data, **kwargs)
return sealedbox_decrypt(data, **kwargs)
def dec_file(name, out=None, **kwargs):
'''
This is a helper function to decrypt a file and return its contents.
You can provide an optional output file using `out`
`name` can be a local file or when not using `salt-run` can be a url like `salt://`, `https://` etc.
CLI Examples:
.. code-block:: bash
salt-run nacl.dec_file name=/tmp/id_rsa.nacl
salt-call nacl.dec_file name=salt://crt/mycert.nacl out=/tmp/id_rsa
salt-run nacl.dec_file name=/tmp/id_rsa.nacl box_type=secretbox \
sk_file=/etc/salt/pki/master/nacl.pub
'''
try:
data = __salt__['cp.get_file_str'](name)
except Exception as e:
# likly using salt-run so fallback to local filesystem
with salt.utils.files.fopen(name, 'rb') as f:
data = salt.utils.stringutils.to_unicode(f.read())
d = dec(data, **kwargs)
if out:
if os.path.isfile(out):
raise Exception('file:{0} already exist.'.format(out))
with salt.utils.files.fopen(out, 'wb') as f:
f.write(salt.utils.stringutils.to_bytes(d))
return 'Wrote: {0}'.format(out)
return d
def sealedbox_encrypt(data, **kwargs):
'''
Encrypt data using a public key generated from `nacl.keygen`.
The encryptd data can be decrypted using `nacl.sealedbox_decrypt` only with the secret key.
CLI Examples:
.. code-block:: bash
salt-run nacl.sealedbox_encrypt datatoenc
salt-call --local nacl.sealedbox_encrypt datatoenc pk_file=/etc/salt/pki/master/nacl.pub
salt-call --local nacl.sealedbox_encrypt datatoenc pk='vrwQF7cNiNAVQVAiS3bvcbJUnF0cN6fU9YTZD9mBfzQ='
'''
# ensure data is in bytes
data = salt.utils.stringutils.to_bytes(data)
pk = _get_pk(**kwargs)
b = libnacl.sealed.SealedBox(pk)
return base64.b64encode(b.encrypt(data))
def sealedbox_decrypt(data, **kwargs):
'''
Decrypt data using a secret key that was encrypted using a public key with `nacl.sealedbox_encrypt`.
CLI Examples:
.. code-block:: bash
salt-call nacl.sealedbox_decrypt pEXHQM6cuaF7A=
salt-call --local nacl.sealedbox_decrypt data='pEXHQM6cuaF7A=' sk_file=/etc/salt/pki/master/nacl
salt-call --local nacl.sealedbox_decrypt data='pEXHQM6cuaF7A=' sk='YmFkcGFzcwo='
'''
if data is None:
return None
# ensure data is in bytes
data = salt.utils.stringutils.to_bytes(data)
sk = _get_sk(**kwargs)
keypair = libnacl.public.SecretKey(sk)
b = libnacl.sealed.SealedBox(keypair)
return b.decrypt(base64.b64decode(data))
def secretbox_encrypt(data, **kwargs):
'''
Encrypt data using a secret key generated from `nacl.keygen`.
The same secret key can be used to decrypt the data using `nacl.secretbox_decrypt`.
CLI Examples:
.. code-block:: bash
salt-run nacl.secretbox_encrypt datatoenc
salt-call --local nacl.secretbox_encrypt datatoenc sk_file=/etc/salt/pki/master/nacl
salt-call --local nacl.secretbox_encrypt datatoenc sk='YmFkcGFzcwo='
'''
# ensure data is in bytes
data = salt.utils.stringutils.to_bytes(data)
sk = _get_sk(**kwargs)
b = libnacl.secret.SecretBox(sk)
return base64.b64encode(b.encrypt(data))
def secretbox_decrypt(data, **kwargs):
'''
Decrypt data that was encrypted using `nacl.secretbox_encrypt` using the secret key
that was generated from `nacl.keygen`.
CLI Examples:
.. code-block:: bash
salt-call nacl.secretbox_decrypt pEXHQM6cuaF7A=
salt-call --local nacl.secretbox_decrypt data='pEXHQM6cuaF7A=' sk_file=/etc/salt/pki/master/nacl
salt-call --local nacl.secretbox_decrypt data='pEXHQM6cuaF7A=' sk='YmFkcGFzcwo='
'''
if data is None:
return None
# ensure data is in bytes
data = salt.utils.stringutils.to_bytes(data)
key = _get_sk(**kwargs)
b = libnacl.secret.SecretBox(key=key)

View File

@ -65,3 +65,65 @@ class NaclTest(ModuleCase):
sk=sk,
)
self.assertEqual(unencrypted_data, ret)
def test_sealedbox_enc_dec(self):
'''
Generate keys, encrypt, then decrypt.
'''
# Store the data
ret = self.run_function(
'nacl.keygen',
)
self.assertIn('pk', ret)
self.assertIn('sk', ret)
pk = ret['pk']
sk = ret['sk']
unencrypted_data = salt.utils.stringutils.to_bytes('hello')
# Encrypt with pk
ret = self.run_function(
'nacl.sealedbox_encrypt',
data=unencrypted_data,
pk=pk,
)
encrypted_data = ret
# Decrypt with sk
ret = self.run_function(
'nacl.sealedbox_decrypt',
data=encrypted_data,
sk=sk,
)
self.assertEqual(unencrypted_data, ret)
def test_secretbox_enc_dec(self):
'''
Generate keys, encrypt, then decrypt.
'''
# Store the data
ret = self.run_function(
'nacl.keygen',
)
self.assertIn('pk', ret)
self.assertIn('sk', ret)
pk = ret['pk']
sk = ret['sk']
unencrypted_data = salt.utils.stringutils.to_bytes('hello')
# Encrypt with pk
ret = self.run_function(
'nacl.secretbox_encrypt',
data=unencrypted_data,
sk=sk,
)
encrypted_data = ret
# Decrypt with sk
ret = self.run_function(
'nacl.secretbox_decrypt',
data=encrypted_data,
sk=sk,
)
self.assertEqual(unencrypted_data, ret)

View File

@ -88,3 +88,65 @@ class NaclTest(ShellCase):
)
self.assertIn('return', ret)
self.assertEqual(unencrypted_data, ret['return'])
def test_sealedbox_enc_dec(self):
'''
Generate keys, encrypt, then decrypt.
'''
# Store the data
ret = self.run_run_plus(
'nacl.keygen',
)
self.assertIn('pk', ret['return'])
self.assertIn('sk', ret['return'])
pk = ret['return']['pk']
sk = ret['return']['sk']
unencrypted_data = 'hello'
# Encrypt with pk
ret = self.run_run_plus(
'nacl.sealedbox_encrypt',
data=unencrypted_data,
pk=pk,
)
encrypted_data = ret['return']
# Decrypt with sk
ret = self.run_run_plus(
'nacl.sealedbox_decrypt',
data=encrypted_data,
sk=sk,
)
self.assertEqual(unencrypted_data, ret['return'])
def test_secretbox_enc_dec(self):
'''
Generate keys, encrypt, then decrypt.
'''
# Store the data
ret = self.run_run_plus(
'nacl.keygen',
)
self.assertIn('pk', ret['return'])
self.assertIn('sk', ret['return'])
pk = ret['return']['pk']
sk = ret['return']['sk']
unencrypted_data = 'hello'
# Encrypt with pk
ret = self.run_run_plus(
'nacl.secretbox_encrypt',
data=unencrypted_data,
sk=sk,
)
encrypted_data = ret['return']
# Decrypt with sk
ret = self.run_run_plus(
'nacl.secretbox_decrypt',
data=encrypted_data,
sk=sk,
)
self.assertEqual(unencrypted_data, ret['return'])