Created
September 7, 2011 15:50
-
-
Save yath/1200944 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/perl | |
use strict; | |
use warnings; | |
sub WINDOWS() { $^O eq "MSWin32" } | |
use Inline C => "DATA", | |
CCFLAGS => "-W -Wall -Wno-unused-variable -Wno-unused-parameter -Wno-comment", | |
BUILD_NOISY => 1, | |
WINDOWS ? | |
() : | |
(LIBS => "-lnss3", | |
INC => "-I/usr/include/nspr"); | |
use MIME::Base64; | |
use DBI; | |
use File::Spec::Functions qw(catfile); | |
use Storable; | |
use File::KeePass; | |
use Digest::SHA1 qw(sha1); | |
use Getopt::Long qw(:config no_auto_abbrev bundling); | |
# comand-line options | |
my $ffprofile; | |
my $kpdbfile; | |
my $kpdbpass; | |
my $defgroup; | |
my $kpchanged = 0; | |
BEGIN { | |
for my $s (qw(NSS_Init NSS_InitReadWrite NSS_Shutdown | |
PK11SDR_Decrypt PK11SDR_Encrypt | |
install_pw_handler | |
win_init win_set_echo)) { | |
eval qq{sub $s { goto &C_$s }}; | |
} | |
require Term::ReadKey unless WINDOWS; | |
} | |
sub DEBUG { | |
warn "@_\n"; | |
} | |
sub encode_base64_oneline { | |
my $ret = encode_base64($_[0]); | |
$ret =~ s/[\r\n]//g; | |
return $ret; | |
} | |
sub storeinfo { | |
my $base64 = encode_base64_oneline(Storable::freeze($_[0])); | |
return "#keeweasel#1#".$base64."#"; | |
} | |
sub fetchinfo { | |
$_[0] =~ /^#keeweasel#(\d+)#([A-Za-z0-9+\/_=\r\n-]+)/ or die "Unable to fetch info"; | |
$1 == 1 or die "Unknown version $1"; | |
return Storable::thaw(decode_base64($2)); | |
} | |
sub get_firefox_profdir { | |
my ($profname) = @_; | |
my $path = do { | |
if ($profname && -d $profname) { | |
# if $profname is a directory use that | |
$profname | |
} else { | |
my $ffroot = catfile(WINDOWS ? | |
($ENV{APPDATA}, "Mozilla", "Firefox") : | |
($ENV{HOME}, ".mozilla", "firefox")); | |
open(my $fh, "<", catfile($ffroot, "profiles.ini")) or | |
die "Unable to open profiles.ini: $!"; | |
my %profiles; | |
my $section; | |
while (<$fh>) { | |
s/[\s\r\n]+$//; | |
next if /^$/; | |
if (/^\[(.*?)\]$/) { | |
$section = $1; | |
} elsif (/^(\w+)=(.*)$/) { | |
die "key-value pair not in any section" unless $section; | |
$profiles{$section}->{$1} = $2; | |
} else { | |
warn "unknown line: $_"; | |
} | |
} | |
close($fh); | |
my @profiles = grep { ($profname && exists $profiles{$_}->{Name}) ? | |
(lc $profiles{$_}->{Name} eq $profname) : | |
$profiles{$_}->{Default} | |
} keys %profiles; | |
die "More than one matching firefox profile found" if @profiles > 1; | |
# if only one profile is defined use that one | |
@profiles = grep /^Profile\d+$/, keys %profiles unless @profiles; | |
die "No default firefox profile found" unless @profiles == 1; | |
my %p = %{$profiles{$profiles[0]}}; | |
$p{IsRelative} ? catfile($ffroot, $p{Path}) : $p{Path} | |
} # -d $profdir | |
}; # $path = do { | |
foreach (qw(key3.db signons.sqlite)) { | |
my $fn = catfile($path, $_); | |
die "$fn is not readable" unless -r $fn; | |
} | |
return $path; | |
} | |
sub set_terminal_echo { | |
my ($echo) = @_; | |
if (WINDOWS) { | |
win_set_echo($echo); | |
} else { | |
Term::ReadKey::ReadMode($echo ? "restore" : "noecho"); | |
} | |
} | |
my %passcache; # cached passwords | |
sub ask_pass { | |
my ($obj, $prompt) = @_; | |
return $passcache{$obj} if exists $passcache{$obj}; | |
print $prompt; | |
set_terminal_echo(0); | |
my $ret = <>; | |
set_terminal_echo(1); | |
print "\n"; | |
chomp $ret; | |
$passcache{$obj} = $ret; | |
return $ret; | |
} | |
sub open_firefox_db { | |
my ($profdir) = @_; | |
my $dbh = DBI->connect("dbi:SQLite:dbname=". | |
catfile($profdir, "signons.sqlite"), | |
"", "", { RaiseError => 1 }); | |
return $dbh; | |
} | |
sub get_firefox_pws { | |
my $dbh = shift; | |
my $ret = $dbh->selectall_hashref("select * from moz_logins", "guid"); | |
foreach my $key (keys %$ret) { | |
$ret->{$key}->{_username} = $ret->{$key}->{encType} == 1 ? | |
PK11SDR_Decrypt(decode_base64($ret->{$key}->{encryptedUsername})) : | |
decode_base64($ret->{$key}->{encryptedUsername}); | |
$ret->{$key}->{_password} = $ret->{$key}->{encType} == 1 ? | |
PK11SDR_Decrypt(decode_base64($ret->{$key}->{encryptedPassword})) : | |
decode_base64($ret->{$key}->{encryptedPassword}); | |
$ret->{$key}->{_id} = $ret->{$key}->{id}; | |
delete $ret->{$key}->{$_} foreach qw(encryptedUsername encryptedPassword id); | |
} | |
return $ret; | |
} | |
sub add_firefox_pw { | |
my ($dbh, $info) = @_; | |
$info->{encryptedUsername} = $info->{encType} == 1 ? | |
encode_base64_oneline(PK11SDR_Encrypt($info->{_username})) : | |
encode_base64_oneline($info->{_username}); | |
$info->{encryptedPassword} = $info->{encType} == 1 ? | |
encode_base64_oneline(PK11SDR_Encrypt($info->{_password})) : | |
encode_base64_oneline($info->{_password}); | |
my @keys = grep !/^_/, keys %$info; | |
$dbh->do("insert into moz_logins(".join(",", @keys).") values (".join(",", ("?")x@keys).")", | |
undef, map { $info->{$_} } @keys) or die $DBI::errstr; | |
} | |
sub open_keepass_db { | |
my ($filename, $password) = @_; | |
my $k = File::KeePass->new; | |
if ($File::KeePass::VERSION <= 0.03) { | |
# workaround for CPAN bug #67534 | |
open (my $fh, "<", $filename) || die "Unable to open $filename: $!"; | |
binmode($fh) || die "Unable to set $kpdbfile to binary mode: $!"; | |
my $buf = do { local $/; <$fh> }; | |
close($fh); | |
$k->parse_db($buf, $password); | |
} else { | |
$k->load_db($filename, $password); | |
} | |
$k->unlock; | |
return $k; | |
} | |
sub save_keepass_db { | |
my ($kpdb, $kpdbfile, $kpdbpass) = @_; | |
# check whether File::KeePass is affected by cpan bug #67553 | |
eval { | |
my $tmpfkp = File::KeePass->new(); | |
my $group = { title => "keeweaseltest".time() }; | |
$tmpfkp->add_group($group); | |
($tmpfkp->find_groups($group))[0]->{unknown}->{23} = "\x68\x61\x69\x6c\x00\x65\x72\x69\x73"; | |
my $buf = $tmpfkp->gen_db("fnord"); | |
$tmpfkp = File::KeePass->new(); | |
$tmpfkp->parse_db($buf, "fnord"); | |
die "size mismatch" if length(($tmpfkp->find_groups($group))[0]->{unknown}->{23}) != 7; | |
}; | |
if ($@) { | |
warn "Your version of File::KeePass is affected by cpan bug #67553.\n". | |
"keeweasel will try to work around this issue.\n"; | |
foreach my $group ($kpdb->find_groups({})) { | |
$group->{unknown}->{$_} = pack("L", length($group->{unknown}->{$_})).$group->{unknown}->{$_} | |
foreach keys %{$group->{unknown}}; | |
foreach my $item ($group->{items}) { | |
$item->{unknown}->{$_} = pack("L", length($item->{unknown}->{$_})).$item->{unknown}->{$_} | |
foreach keys %{$item->{unknown}}; | |
} | |
} | |
} | |
my $tempfile = $kpdbfile.".keeweasel.tmp.".int(time()); | |
if ($File::KeePass::VERSION <= 0.03) { | |
# workaround for CPAN bug #67534 | |
open(my $fh, ">", $tempfile) || die "Unable to open $tempfile: $!"; | |
binmode($fh) || die "Unable to set $kpdbfile to binary mode: $!"; | |
print $fh $kpdb->gen_db($kpdbpass); | |
close($fh) || die "Unable to close $tempfile: $!"; | |
} else { | |
$kpdb->save_db($tempfile, $kpdbpass); | |
} | |
# try loading the file, just in case... | |
open_keepass_db($tempfile, $kpdbpass) || die "Unable to read $tempfile"; | |
rename($tempfile, $kpdbfile) || die "Unable to rename $tempfile to $kpdbfile: $!"; | |
} | |
sub get_keepass_pws { | |
my ($group, $ret) = @_; | |
foreach my $entry (@{$group->{entries}}) { | |
$entry->{comment} =~ /^#keeweasel#/ or next; | |
my $info = fetchinfo($entry->{comment}); | |
push(@{$ret->{$info->{guid}}}, { | |
info => $info, | |
entry => $entry, | |
group => $group | |
}); | |
} | |
get_keepass_pws($_, $ret) foreach @{$group->{groups}}; | |
} | |
sub add_keepass_pw { | |
my ($kpdb, $ffentry, $group, $template) = @_; | |
my $new = { | |
comment => storeinfo({%$ffentry, _last_keepass_pw => sha1($ffentry->{_username}.":".$ffentry->{_password})}), | |
title => $ffentry->{hostname}, | |
url => $ffentry->{hostname}, | |
username => $ffentry->{_username}, | |
password => $ffentry->{_password}, | |
group => $group, | |
}; | |
if ($template) { | |
$new->{$_} = $template->{$_} foreach grep !/^(_|username$|password$|comment$|id$|group$)/, keys %$template; | |
} | |
$kpdb->add_entry($new); | |
$kpchanged = 1; | |
} | |
sub get_keepass_defgroup { | |
my ($kpdb) = @_; | |
$defgroup && ref $defgroup eq "HASH" && return $defgroup; | |
if (!$defgroup) { | |
DEBUG("No group given, using first one found"); | |
return $defgroup = $kpdb->groups->[0]; | |
} | |
my @groups = $kpdb->find_groups({title => $defgroup}); | |
if (@groups > 0) { | |
DEBUG("More than one group with title '$defgroup' found, using first one") | |
if @groups > 1; | |
return $defgroup = $groups[0]; | |
} else { | |
die "No matching group with title '$defgroup' found"; | |
} | |
} | |
sub compare_entries { | |
my ($kpentry, $kpdb, $ffentry, $ffdb) = @_; | |
if (!$ffentry) { | |
DEBUG("$kpentry->{info}->{guid} is new to firefox, adding..."); | |
add_firefox_pw($ffdb, $kpentry->{info}); | |
} elsif (!$kpentry) { | |
DEBUG("$ffentry->{guid} is new to keepass, adding..."); | |
add_keepass_pw($kpdb, $ffentry, get_keepass_defgroup($kpdb)); | |
} elsif ($ffentry->{timePasswordChanged} > $kpentry->{info}->{timePasswordChanged}) { | |
DEBUG("password changed in firefox for $ffentry->{guid}, updating in keepass..."); | |
add_keepass_pw($kpdb, $ffentry, $kpdb->{group}, $kpdb->{entry}); | |
$kpdb->delete_entry({id => $kpentry->{entry}->{id}}); | |
} elsif ($kpentry->{info}->{timePasswordChanged} > $ffentry->{timePasswordChanged}) { | |
DEBUG("password changed in keepass for $ffentry->{guid}, updating in firefox..."); | |
add_firefox_pw($ffdb, $kpentry->{info}); | |
$ffdb->do("delete from moz_logins where id = ?", undef, $ffentry->{_id}) | |
or die "Unable to delete login: $DBI::errstr"; | |
} | |
} | |
sub sync_pws { | |
my ($kpdb, $ffdb) = @_; | |
my $ffpws = get_firefox_pws($ffdb); | |
my $kppws = {}; | |
get_keepass_pws($_, $kppws) foreach @{$kpdb->groups}; | |
my %all_guids = map { $_ => 1 } (keys %$ffpws, keys %$kppws); | |
foreach my $guid (keys %all_guids) { | |
# force an undef entry on either side to make sure compare_entries | |
# gets called | |
push(@{$kppws->{$guid}}, undef) unless | |
exists($kppws->{$guid}) && @{$kppws->{$guid}}; | |
$ffpws->{$guid} = undef unless exists $ffpws->{$guid}; | |
compare_entries($_, $kpdb, $ffpws->{$guid}, $ffdb) | |
foreach @{$kppws->{$guid}}; | |
} | |
} | |
sub main { | |
GetOptions("p|ffprofile=s" => \$ffprofile, | |
"k|keepassdb=s" => \$kpdbfile, | |
"d|defgroup=s" => \$defgroup, | |
"P|kpdbpass=s" => \$kpdbpass | |
); | |
die "KeePass DB file required" unless $kpdbfile; | |
win_init if WINDOWS; | |
my $ffprofdir = get_firefox_profdir($ffprofile); | |
DEBUG("using firefox profile $ffprofdir"); | |
install_pw_handler(); | |
NSS_Init($ffprofdir); | |
# do an NSS call to check for common errors and trigger password prompt | |
# if necessary | |
eval { PK11SDR_Encrypt("test"); }; | |
if ($@ && $@ =~ /SEC_ERROR_READ_ONLY/) { | |
# PK11SDR_Encrypt failed with SEC_ERROR_READ_ONLY, this happens when no key | |
# has been generated yet in the key3.db and PK11SDR_Encrypt tries to generate | |
# one. re-init NSS read-write in this case. | |
NSS_Shutdown(); | |
NSS_InitReadWrite($ffprofdir); | |
print "Your key3.db will probably be updated - restart Firefox soon!\n"; | |
} elsif ($@ && $@ =~ /SEC_ERROR_BAD_PASSWORD/) { | |
# wrong password | |
die "Firefox password is incorrect"; | |
} elsif ($@) { | |
die $@; # any other error | |
} | |
my $ffdb = open_firefox_db($ffprofdir); | |
$kpdbpass = ask_pass($kpdbfile, "Enter password for KeePass DB: ") unless $kpdbpass; | |
my $kpdb = eval { open_keepass_db($kpdbfile, $kpdbpass) }; | |
if ($@ && $@ =~ /file checksum did not match/) { | |
die "KeePass password is incorrect or file is corrupted"; | |
} elsif ($@) { | |
die $@; | |
} | |
sync_pws($kpdb, $ffdb); | |
save_keepass_db($kpdb, $kpdbfile, $kpdbpass) if $kpchanged; | |
} | |
main | |
__DATA__ | |
__C__ | |
#define UNUSED(x) PERL_UNUSED_VAR(x) | |
#ifdef WIN32 | |
#include <windows.h> | |
#include <Strsafe.h> | |
#define BUFSIZE 1024 | |
#define FF_REGKEY TEXT("SOFTWARE\\Mozilla\\Mozilla Firefox") | |
/******* BEGIN NSS/NSPR DEFINITIONS *******/ | |
typedef enum { | |
siBuffer = 0 | |
/* rest omitted */ | |
} SECItemType; | |
typedef enum { | |
SECWouldBlock = -2, | |
SECFailure = -1, | |
SECSuccess = 0 | |
} SECStatus; | |
typedef struct { | |
SECItemType type; | |
unsigned char *data; | |
unsigned int len; | |
} SECItem; | |
typedef int PRBool; | |
typedef int PRErrorCode; | |
typedef struct PK11SlotInfoStr PK11SlotInfo; | |
typedef char *(*PK11PasswordFunc)(PK11SlotInfo *slot, PRBool retry, void *arg); | |
SECStatus (*NSS_Init)(const char *); | |
SECStatus (*NSS_InitReadWrite)(const char *); | |
SECStatus (*NSS_Shutdown)(void); | |
SECStatus (*PK11SDR_Encrypt)(SECItem *, SECItem *, SECItem *, void *); | |
SECStatus (*PK11SDR_Decrypt)(SECItem *, SECItem *, void *); | |
void (*PK11_SetPasswordFunc)(PK11PasswordFunc func); | |
void (*SECITEM_FreeItem)(SECItem *, PRBool); | |
PRErrorCode (*PR_GetError)(void); | |
char * (*PL_strdup)(const char *); | |
#define SEC_ERROR_BAD_PASSWORD -8177 | |
#define SEC_ERROR_READ_ONLY -8126 | |
/******* END NSS/NSPR DEFINITIONS *******/ | |
LPTSTR getRegKey(HKEY key, LPCTSTR subkey, LPCTSTR value) { | |
HKEY h_subkey; | |
static TCHAR data[BUFSIZE]; | |
DWORD type; | |
DWORD cb_data = sizeof(data); | |
LONG rc = RegOpenKeyEx( | |
key, // hKey | |
subkey, // lpSubkey | |
0, // ulOptions | |
KEY_READ, // samDesired | |
&h_subkey // phkResult | |
); | |
if (rc != ERROR_SUCCESS) | |
return NULL; | |
rc = RegQueryValueEx( | |
h_subkey, // hKey | |
value, // lpValueName | |
NULL, // lpReserved | |
&type, // lpType | |
(LPBYTE)data, // lpData | |
&cb_data // lpCbData | |
); | |
RegCloseKey(h_subkey); | |
if (rc != ERROR_SUCCESS) | |
return NULL; | |
if (type != REG_SZ) | |
croak("%s is not a REG_SZ?!", subkey); | |
return data; | |
} | |
LPTSTR getFirefoxDirectory() { | |
LPTSTR ffversion = getRegKey(HKEY_LOCAL_MACHINE, FF_REGKEY, TEXT("CurrentVersion")); | |
if (!ffversion) | |
croak("Unable to open HKLM\\%s\\CurrentVersion: %d", FF_REGKEY, (int)GetLastError()); | |
TCHAR ffkeypath[BUFSIZE]; | |
if (StringCchPrintf(ffkeypath, sizeof(ffkeypath), TEXT("%s\\%s\\Main"), FF_REGKEY, ffversion) != S_OK) | |
croak("StringCchPrintf failed"); | |
LPTSTR ffdir = getRegKey(HKEY_LOCAL_MACHINE, ffkeypath, TEXT("Install Directory")); | |
if (!ffdir) | |
croak("Unable to open HKLM\\%s\\Install Directory: %d", ffkeypath, (int)GetLastError()); | |
return ffdir; | |
} | |
void C_win_init() { | |
#define LOAD(name, fatal) \ | |
if (StringCchPrintf(dllpath, sizeof(dllpath), TEXT("%s\\%s.dll"), ffdir, #name) != S_OK) \ | |
croak("StringCchPrintf failed for " #name ".dll"); \ | |
HMODULE dll_ ## name = LoadLibrary(dllpath); \ | |
if (fatal && ! dll_ ## name) \ | |
croak("LoadLibrary(\"" #name ".dll\") failed: %d", (int)GetLastError()); | |
#define IMPORT(dll, func) do { \ | |
if (!(func = (void *)GetProcAddress(dll_ ## dll, #func))) \ | |
croak("GetProcAddress(" #dll ", \"" #func "\") failed: %d", (int)GetLastError()); \ | |
} while(0) | |
LPTSTR ffdir = getFirefoxDirectory(); | |
TCHAR dllpath[BUFSIZE]; | |
LOAD(mozcrt19, 0) | |
LOAD(nspr4, 1) | |
LOAD(plc4, 1) | |
LOAD(plds4, 0) | |
LOAD(nssutil3, 0) | |
LOAD(sqlite3, 0) | |
LOAD(mozsqlite3, 0) | |
LOAD(softokn3, 0) | |
LOAD(nss3, 1) | |
IMPORT(nss3, NSS_Init); | |
IMPORT(nss3, NSS_InitReadWrite); | |
IMPORT(nss3, NSS_Shutdown); | |
IMPORT(nss3, PK11SDR_Encrypt); | |
IMPORT(nss3, PK11SDR_Decrypt); | |
IMPORT(nss3, PK11_SetPasswordFunc); | |
IMPORT(nss3, SECITEM_FreeItem); | |
IMPORT(nspr4, PR_GetError); | |
IMPORT(plc4, PL_strdup); | |
} | |
void C_win_set_echo(SV *echo) { | |
DWORD mode; | |
HANDLE console; | |
if (!(console = GetStdHandle(STD_INPUT_HANDLE))) | |
croak("Unable to get STD_INPUT_HANDLE: %d", (int)GetLastError()); | |
if (!GetConsoleMode(console, &mode)) | |
croak("GetConsoleMode failed: %d", (int)GetLastError()); | |
if (SvTRUE(echo)) | |
mode |= ENABLE_ECHO_INPUT; | |
else | |
mode &= ~ENABLE_ECHO_INPUT; | |
if (!SetConsoleMode(console, mode)) | |
croak("SetConsoleMode failed: %d", (int)GetLastError()); | |
} | |
#else /* WIN32 */ | |
#include <nss/nss.h> | |
#include <nss/pk11sdr.h> | |
#include <nss/secerr.h> | |
#include <nss/secmodt.h> | |
#include <nss/pk11pub.h> | |
#include <nspr/nspr.h> | |
void C_win_init() { | |
croak("win_init makes only sense on WIN32"); | |
} | |
void C_win_set_echo(SV *echo) { | |
UNUSED(echo); | |
croak("win_set_echo makes only sense on WIN32"); | |
} | |
#endif /* WIN32 */ | |
char *last_db = NULL; | |
char *pk11_pw_handler(PK11SlotInfo *slot, PRBool retry, void *arg) { | |
dSP; | |
int count; | |
SV *pass; | |
char *ret; | |
UNUSED(slot); | |
UNUSED(arg); | |
if (retry) | |
return NULL; | |
if (!last_db) | |
croak("pk11_pw_handler called without an open database"); | |
ENTER; | |
SAVETMPS; | |
PUSHMARK(SP); | |
XPUSHs(sv_2mortal(newSVpv(last_db, 0))); | |
XPUSHs(sv_2mortal(newSVpvs("Enter password for Firefox DB: "))); | |
PUTBACK; | |
count = call_pv("ask_pass", G_SCALAR); | |
SPAGAIN; | |
if (count != 1) | |
croak("Uhm, ask_pass() didn't return exactly 1 but %d", count); | |
pass = POPs; | |
ret = PL_strdup(SvPV_nolen(pass)); | |
PUTBACK; | |
FREETMPS; | |
LEAVE; | |
return ret; | |
} | |
void C_install_pw_handler() { | |
PK11_SetPasswordFunc(pk11_pw_handler); | |
} | |
void C_NSS_Init(char *path) { | |
if (NSS_Init(path) != SECSuccess) | |
croak("NSS_Init(\"%s\") failed: %d", path, PR_GetError()); | |
last_db = path; | |
} | |
void C_NSS_InitReadWrite(char *path) { | |
if (NSS_InitReadWrite(path) != SECSuccess) | |
croak("NSS_InitReadWrite(\"%s\") failed: %d", path, PR_GetError()); | |
last_db = path; | |
} | |
void C_NSS_Shutdown() { | |
if (NSS_Shutdown() != SECSuccess) | |
croak("NSS_Shutdown() failed: %d", PR_GetError()); | |
last_db = NULL; | |
} | |
SV *C_PK11SDR_Decrypt(SV *enc) { | |
SECItem si_enc, si_dec; | |
si_enc.type = siBuffer; | |
si_enc.data = (unsigned char *)SvPV(enc, si_enc.len); | |
if (PK11SDR_Decrypt(&si_enc, &si_dec, NULL) != SECSuccess) | |
croak("PK11SDR_Decrypt(encrypted, decrypted, NULL) failed: %d", PR_GetError()); | |
SV *ret = newSVpvn((char *)si_dec.data, si_dec.len); | |
SECITEM_FreeItem(&si_dec, 0); | |
return ret; | |
} | |
SV *C_PK11SDR_Encrypt(SV *decrypted, ...) { | |
Inline_Stack_Vars; | |
SECItem si_key, si_dec, si_enc; | |
SV *key; | |
switch (Inline_Stack_Items) { | |
case 1: | |
key = &PL_sv_undef; | |
break; | |
case 2: | |
key = Inline_Stack_Item(1); | |
break; | |
default: | |
croak("Usage: PK11SDR_Encrypt(decrypted [, key ID])"); | |
break; | |
} | |
si_key.type = siBuffer; | |
if (SvOK(key)) { | |
si_key.data = (unsigned char *)SvPV(key, si_key.len); | |
} else { | |
si_key.len = 0; | |
} | |
si_dec.type = siBuffer; | |
si_dec.data = (unsigned char *)SvPV(decrypted, si_dec.len); | |
if (PK11SDR_Encrypt(&si_key, &si_dec, &si_enc, NULL) != SECSuccess) { | |
PRErrorCode err = PR_GetError(); | |
// put known common error codes into the error message so the caller | |
// can handle then | |
croak("PK11SDR_Encrypt(key, decrypted, encrypted, NULL) failed: %d%s", err, | |
(err == SEC_ERROR_READ_ONLY) ? " (SEC_ERROR_READ_ONLY)" : | |
(err == SEC_ERROR_BAD_PASSWORD) ? " (SEC_ERROR_BAD_PASSWORD)" : | |
""); | |
} | |
SV *ret = newSVpvn((char *)si_enc.data, si_enc.len); | |
SECITEM_FreeItem(&si_enc, 0); | |
return ret; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment