| #!/usr/bin/env python3 |
| |
| # A little tool to encrypt/decrypt git secrets. Kinda like password-store, but more purpose specific and portable. |
| |
| import logging |
| import os |
| import sys |
| import subprocess |
| |
| keys = [ |
| "63DFE737F078657CC8A51C00C29ADD73B3563D82", # q3k |
| "482FF104C29294AD1CAF827BA43890A3DE74ECC7", # inf |
| "F07205946C07EEB2041A72FBC60C64879534F768", # cz2 |
| "0879F9FCA1C836677BB808C870FD60197E195C26", # implr |
| ] |
| |
| |
| logger = logging.getLogger(__name__) |
| |
| |
| def encrypt(src, dst): |
| cmd = ['gpg' , '--encrypt', '--armor', '--batch', '--yes', '--output', dst] |
| for k in keys: |
| cmd.append('--recipient') |
| cmd.append(k) |
| cmd.append(src) |
| subprocess.check_call(cmd) |
| |
| def decrypt(src, dst): |
| cmd = ['gpg', '--decrypt', '--batch', '--yes', '--output', dst, src] |
| subprocess.check_call(cmd) |
| |
| |
| class SecretStoreMissing(Exception): |
| pass |
| |
| |
| class SecretStore(object): |
| def __init__(self, plain_root, cipher_root): |
| self.proot = plain_root |
| self.croot = cipher_root |
| |
| def exists(self, suffix): |
| p = os.path.join(self.proot, suffix) |
| c = os.path.join(self.croot, suffix) |
| return os.path.exists(c) or os.path.exists(p) |
| |
| def plaintext(self, suffix): |
| p = os.path.join(self.proot, suffix) |
| c = os.path.join(self.croot, suffix) |
| |
| has_p = os.path.exists(p) |
| has_c = os.path.exists(c) |
| |
| if has_c and has_p and os.path.getctime(p) < os.path.getctime(c): |
| logger.info("Decrypting {} ({})...".format(suffix, c)) |
| decrypt(c, p) |
| |
| return p |
| |
| def open(self, suffix, mode, *a, **kw): |
| p = os.path.join(self.proot, suffix) |
| c = os.path.join(self.croot, suffix) |
| if 'w' in mode: |
| return open(p, mode, *a, **kw) |
| |
| if not self.exists(suffix): |
| raise SecretStoreMissing("Secret {} does not exist".format(suffix)) |
| |
| if not os.path.exists(p) or os.path.getctime(p) < os.path.getctime(c): |
| logger.info("Decrypting {} ({})...".format(suffix, c)) |
| decrypt(c, p) |
| |
| return open(p, mode, *a, **kw) |
| |
| |
| def main(): |
| if len(sys.argv) < 3 or sys.argv[1] not in ('encrypt', 'decrypt'): |
| sys.stderr.write("Usage: {} encrypt/decrypt file\n".format(sys.argv[0])) |
| sys.stderr.flush() |
| return 1 |
| |
| action = sys.argv[1] |
| src = sys.argv[2] |
| |
| if action == 'encrypt': |
| encrypt(src, '-') |
| else: |
| decrypt(src, '-') |
| |
| if __name__ == '__main__': |
| sys.exit(main() or 0) |