Config::PitをRC4で暗号化

Config::Pit、すごい便利だなーと思いつつも、でも平文なんだよな、パスワードとか保存するのイヤだなーと思ってて、ずっと躊躇していたんだけど、やっぱり使いたいので暗号化することにしました。

とりあえずRC4で。根本的な解決ではないけど、平文で保存するよりはずっと安心感あるかなと。

参考:Crypt::RC4を使ってみる - XXXannex

package Config::Pit::RC4;

use strict;
use warnings;
use Crypt::RC4;
use base qw(Config::Pit Exporter);

our @EXPORT     = qw/pit_get pit_set pit_switch/;
our $passphrase = 'password';
our $set_done   = 0;

*pit_get    = \&get;
*pit_set    = \&set;
*pit_switch = \&switch;

sub get {
  my ($name, %opts) = @_;
  my $config;

  # override Config::Pit::set and call original get
  {
    no warnings 'redefine';
    my $org_method    = Config::Pit->can('set');
    *Config::Pit::set = sub {$set_done=1; &$org_method(@_)};
    $config           = Config::Pit::get($name, %opts);
    *Config::Pit::set = $org_method;
  }
  # non-crypted
  if($set_done){
    my $profile = Config::Pit::_load();
    $profile->{$name}->{$_} = encrypt($profile->{$name}->{$_}) foreach (keys %{$profile->{$name}});
    YAML::Syck::DumpFile($Config::Pit::profile_file, $profile);
  }
  # crypted
  else{
    $config->{$_} = decrypt($config->{$_}) foreach (keys %$config);
  }
  return $config;
}

sub set {
  my ($name, %opts) = @_;
  my $config = Config::Pit::set($name, %opts);

  my $profile = Config::Pit::_load();
  $profile->{$name}->{$_} = encrypt($profile->{$name}->{$_}) foreach (keys %{$profile->{$name}});
  YAML::Syck::DumpFile($Config::Pit::profile_file, $profile);
  return $config;
}

sub encrypt{
  my $plaintext = shift;
  my $encrypted = RC4($passphrase, $plaintext);
  $encrypted =~ s/(.)/unpack('H2', $1)/eg;
  return $encrypted;
}

sub decrypt{
  my $encrypted = shift;
  $encrypted =~ s/([0-9A-Fa-f]{2})/pack('H2', $1)/eg;
  my $decrypted = RC4($passphrase, $encrypted);
  return $decrypted;
}
use strict;
use warnings;
use Config::Pit::RC4;

my $config_g = pit_get("get_test", require => {
  username => "foobar",
  password => "barbaz",
});

my $config_s = pit_set("set_test", data => {
    username => "foobar",
    password => "barbaz",
});

### $config_g
### $config_s

実行結果

### $config_g: {
###              password => 'barbaz',
###              username => 'foobar'
###            }
### $config_s: {
###              password => 'barbaz',
###              username => 'foobar'
###            }
$ cat ~/.pit/default.yaml
---
get_test:
  password: 9d944a6c2d44
  username: 999a576c2d4c
set_test:
  password: 9d944a6c2d44
  username: 999a576c2d4c

Config::Pitが継承に対応してくれていればもう少しきれいに書けるのかもしれないけど、とりあえず目的は果たされた。

基本的に、「まずはオリジナルを呼んで、その後暗号化/復号化する」というアレな実装です。getの中で、パスワードが未設定だとsetを呼んで設定する部分があるので、若干めんどくさい感じに。