Commit 6d0636f

mo <mo.khan@gmail.com>
2018-02-11 19:51:34
extract class to encrypt/decrypt db on demand.
1 parent 4eb9e1d
lib/tfa/cli.rb
@@ -9,31 +9,23 @@ module TFA
 
     desc "add NAME SECRET", "add a new secret to the database"
     def add(name, secret)
-      open_database do
-        storage.save(name, clean(secret))
-      end
+      storage.save(name, clean(secret))
       "Added #{name}"
     end
 
     desc "destroy NAME", "remove the secret associated with the name"
     def destroy(name)
-      open_database do
-        storage.delete(name)
-      end
+      storage.delete(name)
     end
 
     desc "show NAME", "shows the secret for the given key"
     def show(name = nil)
-      open_database do
-        name ? storage.secret_for(name) : storage.all
-      end
+      name ? storage.secret_for(name) : storage.all
     end
 
     desc "totp NAME", "generate a Time based One Time Password using the secret associated with the given NAME."
     def totp(name = nil)
-      open_database do
-        TotpCommand.new(storage).run(name)
-      end
+      TotpCommand.new(storage).run(name)
     end
 
     desc "now SECRET", "generate a Time based One Time Password for the given secret"
@@ -58,7 +50,6 @@ module TFA
             yaml_storage.save(name, secret) if yes?("Migrate `#{name}`?")
           end
         end
-        yaml_storage.encrypt!(passphrase)
         File.delete(pstore_path) if yes?("Delete `#{pstore_path}`?")
       end
     end
@@ -67,14 +58,14 @@ module TFA
     def encrypt
       return unless ensure_upgraded!
 
-      yaml_storage.encrypt!(passphrase)
+      yaml_storage.encrypt!
     end
 
     desc "decrypt", "decrypts the tfa database"
     def decrypt
       return unless ensure_upgraded!
 
-      yaml_storage.decrypt!(passphrase)
+      yaml_storage.decrypt!
     end
 
     private
@@ -88,7 +79,7 @@ module TFA
     end
 
     def yaml_storage
-      @yaml_storage ||= Storage.new(yaml_path)
+      @yaml_storage ||= SecureProxy.new(Storage.new(yaml_path), passphrase)
     end
 
     def filename
@@ -131,12 +122,5 @@ module TFA
     def upgraded?
       !File.exist?(pstore_path) && File.exist?(yaml_path)
     end
-
-    def open_database
-      yaml_storage.decrypt!(passphrase) if upgraded?
-      result = yield
-      yaml_storage.encrypt!(passphrase)
-      result
-    end
   end
 end
lib/tfa/secure_proxy.rb
@@ -0,0 +1,53 @@
+module TFA
+  class SecureProxy
+    def initialize(original, passphrase)
+      @original = original
+      @digest = Digest::SHA256.digest(passphrase)
+    end
+
+    def encrypt!
+      cipher = OpenSSL::Cipher.new("AES-256-CBC")
+      cipher.encrypt
+      cipher.key = @digest
+      #iv = cipher.random_iv
+      #cipher.iv = iv
+
+      plain_text = read_all
+      #cipher_text = iv + cipher.update(plain_text) + cipher.final
+      cipher_text = cipher.update(plain_text) + cipher.final
+      flush(cipher_text)
+    end
+
+    def decrypt!
+      return unless File.exist?(@original.path)
+
+      cipher_text = read_all
+      decipher = OpenSSL::Cipher.new("AES-256-CBC")
+      decipher.decrypt
+      #decipher.iv = cipher_text[0..decipher.iv_len-1]
+      decipher.key = @digest
+      #data = cipher_text[decipher.iv_len..-1]
+      data = cipher_text
+      flush(decipher.update(data) + decipher.final)
+    end
+
+    private
+
+    def method_missing(name, *args, &block)
+      super unless @original.respond_to?(name)
+
+      decrypt!
+      result = @original.public_send(name, *args, &block)
+      encrypt!
+      result
+    end
+
+    def read_all
+      IO.read(@original.path)
+    end
+
+    def flush(data)
+      IO.write(@original.path, data)
+    end
+  end
+end
lib/tfa/storage.rb
@@ -43,30 +43,6 @@ module TFA
       end
     end
 
-    def encrypt!(passphrase)
-      cipher = OpenSSL::Cipher.new("AES-256-CBC")
-      cipher.encrypt
-      cipher.key = digest_for(passphrase)
-      #iv = cipher.random_iv
-      #cipher.iv = iv
-
-      plain_text = read_all
-      #cipher_text = iv + cipher.update(plain_text) + cipher.final
-      cipher_text = cipher.update(plain_text) + cipher.final
-      flush(cipher_text)
-    end
-
-    def decrypt!(passphrase)
-      cipher_text = read_all
-      decipher = OpenSSL::Cipher.new("AES-256-CBC")
-      decipher.decrypt
-      #decipher.iv = cipher_text[0..decipher.iv_len-1]
-      decipher.key = digest_for(passphrase)
-      #data = cipher_text[decipher.iv_len..-1]
-      data = cipher_text
-      flush(decipher.update(data) + decipher.final)
-    end
-
     private
 
     def open_readonly
@@ -74,17 +50,5 @@ module TFA
         yield @storage
       end
     end
-
-    def read_all
-      IO.read(path)
-    end
-
-    def flush(data)
-      IO.write(path, data)
-    end
-
-    def digest_for(passphrase)
-      Digest::SHA256.digest(passphrase)
-    end
   end
 end
lib/tfa.rb
@@ -6,6 +6,7 @@ require "rotp"
 require "yaml/store"
 
 require "tfa/cli"
+require "tfa/secure_proxy"
 require "tfa/storage"
 require "tfa/totp_command"
 require "tfa/version"