Advertisement
Guest User

Untitled

a guest
Mar 23rd, 2017
92
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 4.68 KB | None | 0 0
  1. """
  2. Keyring backend that uses the Fernet symmetric key cryptography from the
  3. `cryptography` library.
  4.  
  5. This stores a private key for a user at `~/.trigger_key`.
  6.  
  7. The idea is that this could be used for updating .tacacsrc internals to
  8. store/retrieve credentials using the Keyring API and therefore take advantage
  9. of any backend that Keyring supports.
  10.  
  11. This backend could be the default Trigger keyring backend so that Trigger can
  12. operate in a self-contained way (only adding keyring and keyrings.alt libs as
  13. requirements).
  14.  
  15. Requirements:
  16.  
  17. - cryptography
  18. - keyring
  19. - keyrings.alt
  20.  
  21. Run this before testing::
  22.  
  23. pip install cryptography keyring keyrings.alt
  24.  
  25. How to use it:
  26.  
  27. >>> import fernet_keyring
  28. >>> import keyring
  29. >>> keyring.set_keyring(fernet_keyring.FernetKeyring())
  30. >>> keyring.set_password('example', 'admin', 'password')
  31. Reading key from existing keyfile /Users/jathan/.trigger_key
  32. >>> keyring.get_password('example', 'admin')
  33. u'password'
  34. """
  35.  
  36. import getpass
  37. import os
  38. import sys
  39.  
  40. from keyring.py27compat import configparser
  41. from keyring.util import properties
  42. from keyring.util.escape import escape as escape_for_ini
  43. from keyrings.alt import file_base
  44.  
  45.  
  46. __author__ = 'jathan@gmail.com'
  47.  
  48.  
  49. # Where the user's private key is stored.
  50. KEYFILE = os.path.expanduser('~/.trigger_key')
  51.  
  52.  
  53. class FernetKeyring(file_base.Keyring):
  54. """
  55. A Fernet keyring.
  56. """
  57.  
  58. keyfile = KEYFILE
  59. filename = 'fernet_pass.cfg'
  60. pw_prefix = 'pw:'.encode()
  61.  
  62. @properties.ClassProperty
  63. @classmethod
  64. def priority(self):
  65. try:
  66. __import__('cryptography.fernet')
  67. except ImportError:
  68. raise RuntimeError('cryptography required')
  69.  
  70. return .25
  71.  
  72. @properties.NonDataProperty
  73. def keyring_key(self):
  74. if self._check_file():
  75. self._unlock()
  76. else:
  77. self._init_file()
  78. return self.keyring_key
  79.  
  80. def _create_cipher(self, key):
  81. from cryptography.fernet import Fernet
  82. return Fernet(key)
  83.  
  84. def _get_new_password(self):
  85. if not os.path.exists(self.keyfile):
  86. print 'Creating new keyfile', self.keyfile
  87. from cryptography.fernet import Fernet
  88. password = Fernet.generate_key()
  89. with open(self.keyfile, 'w') as f:
  90. f.write(password)
  91. os.chmod(self.keyfile, 0600)
  92. else:
  93. print 'Reading key from existing keyfile', self.keyfile
  94. with open(self.keyfile, 'r') as f:
  95. password = f.read()
  96. return password
  97.  
  98. def _init_file(self):
  99. """Initialize a new password file and set the reference password."""
  100. self.keyring_key = self._get_new_password()
  101.  
  102. # Set a reference password, used to check that the password provided
  103. # matches for subsequent checks.
  104. self.set_password(
  105. 'keyring-setting', 'password reference', 'password reference value'
  106. )
  107.  
  108. def _check_file(self):
  109. """
  110. Check if the file exists and has the expected password reference.
  111. """
  112. if not os.path.exists(self.file_path):
  113. return False
  114.  
  115. self._migrate()
  116.  
  117. config = configparser.RawConfigParser()
  118. config.read(self.file_path)
  119.  
  120. try:
  121. config.get(
  122. escape_for_ini('keyring-setting'),
  123. escape_for_ini('password reference'),
  124. )
  125. except (configparser.NoSectionError, configparser.NoOptionError):
  126. return False
  127. return True
  128.  
  129. def _unlock(self):
  130. """
  131. Unlock this keyring by getting the password for the keyring from the
  132. user.
  133. """
  134. print 'Reading key from existing keyfile', self.keyfile
  135. with open(self.keyfile, 'r') as f:
  136. password = f.read()
  137. self.keyring_key = password
  138.  
  139. try:
  140. ref_pw = self.get_password('keyring-setting', 'password reference')
  141. assert ref_pw == 'password reference value'
  142. except AssertionError:
  143. self._lock()
  144. raise ValueError('Incorrect password')
  145.  
  146. def _lock(self):
  147. """
  148. Remove the keyring key from this instance.
  149. """
  150. del self.keyring_key
  151.  
  152. def encrypt(self, password):
  153. cipher = self._create_cipher(self.keyring_key)
  154. password_encrypted = cipher.encrypt(bytes(self.pw_prefix + password))
  155. return password_encrypted
  156.  
  157. def decrypt(self, password_encrypted):
  158. cipher = self._create_cipher(self.keyring_key)
  159. password = cipher.decrypt(password_encrypted)
  160. assert password.startswith(self.pw_prefix)
  161. return password[3:]
  162.  
  163. def _migrate(self, keyring_password=None):
  164. """
  165. Convert older keyrings to the current format.
  166. """
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement