radtad has asked for the wisdom of the Perl Monks concerning the following question:

I'm having a problem with Crypt::OpenPGP. The scenario is this:

* Create a group (ADMIN) with it's own public and private keys and group password and them it into the database.

* Create 2 users (USER1, USER2) with public and private keys each with their own individual passwords.

* Then, I add USER1 to the ADMIN group and used USER1's keyid as a recipient for the decrypted ADMIN data. I can decrypt the group data just fine using USER1's password.

* Next, I add USER2 to the ADMIN group and used USER1 and USER2's keyid as a recipient for the decrypted ADMIN data.

Problem: For some reason once I add USER2 to the ADMIN group, I cannot use USER1's password to decrypt the ADMIN data anymore. However, USER2's password does work. Anyone have an Idea why this doesn't work. Crypt::OpenPGP says it handles multiple recipients so I should be able to use any of those recipients passwords to decrypt the ADMIN data correct?

Replies are listed 'Best First'.
Re: Crypt:OpenPGP Recipients Problem
by kyle (Abbot) on Jan 10, 2008 at 22:16 UTC

    I've never used Crypt::OpenPGP, but I know a little about OpenPGP and GnuPG in particular.

    I'm not clear on what you mean when you say you add USER1 to an ADMIN group. GnuPG allows you to specify a group in its config file, but this is like an email alias. When you specify the group as a recipient, GnuPG will treat that as if you had specified all of the group's members as recipients.

    It might help to show more of the procedure you went through to create the group, add keys to it, and how you're invoking Crypt::OpenPGP to create your messages. Crypt::OpenPGP doesn't seem to have any facilities for managing a keyring, so I'm guessing you are using an external tool to create your keys and populate the database you mention. Whatever you can tell us about that might help us solve your problem also.

      Thanks for the input. The code is a little convoluted, so I'll try to include only some of the major parts.
      createGroup('ADMIN','foo'); createUser('USER1','bar'); createUser('USER2','bar2'); addGroupUser('USER1','ADMIN'); decryptGroupData('ADMIN','bar'); sub initPGP { my($uid) = @_; my(%args,$sql,$sth,$ret,$public,$secret,$ring,$kb,$pubring,$secrin +g); if ($uid =~ m#\d{3,6}$#) { $sql = "SELECT public,secret FROM gpg_user WHERE code=$uid"; } else { $sql = "SELECT public,secret FROM gpg_group WHERE group_name='$uid'"; } $sth = runSQL($sql); $ret = $sth->fetchrow_arrayref; if ($ret) { ($public,$secret) = unarmourKeys($ret->[0],$ret->[1]); $pubring = Crypt::OpenPGP::KeyRing->new( Data => $pub +lic ); $secring = Crypt::OpenPGP::KeyRing->new( Data => $sec +ret ); $kb = $pubring->find_keyblock_by_uid($uid); $args{SecRing} = $secring; $args{PubRing} = $pubring; } $args{Compat} = COMPAT; $args{Verbosity} = DEBUG; return(Crypt::OpenPGP->new(%args),$kb); } sub unarmourKeys { my($public,$secret) = @_; my($pubsaved,$secsaved); $pubsaved = uri_unescape($public); $secsaved = uri_unescape($secret); $pubsaved = Crypt::OpenPGP::Armour->unarmour($pubsaved); $secsaved = Crypt::OpenPGP::Armour->unarmour($secsaved); return($pubsaved->{Data},$secsaved->{Data}); } sub addGroupUser { my($code,$grouppass,$group) = @_; my($groupid,$sql); $groupid = getGroupID($group); if ($code && $groupid) { $sql = "INSERT INTO gpg_group_user(groupid,code) VALUES ('$groupid','$code')"; runSQL($sql); encryptGroupData($grouppass,$group); } } sub createUser { my($code,$password) = @_; my($info,$public,$secret,$finger,$keyid,$uid, $sql,$data,$pubring,$secring,@recipients); $info = genKeys($password,$code); $public = $info->{public}; $secret = $info->{secret}; $finger = $info->{finger}; $keyid = $info->{keyid}; if ($finger) { $sql = "INSERT INTO gpg_user(code,public,secret,fingerprint) VALUES ($code,'$public','$secret','$finger')"; runSQL($sql); push(@recipients, $keyid); ($pubring,$secring) = unarmourKeys($public,$secret); print "ENCODED: " . encode_base64($password) . "\n"; $info->{public} = $pubring; $info->{secret} = $secring; $info->{text} = encode_base64($password); $info->{recipients} = \@recipients; $info->{password} = $password; $data = encryptData($code,$info); $sql = "UPDATE gpg_user SET data='$data' WHERE code='$code'"; runSQL($sql); } } sub createGroup { my($group, $password) = @_; my($sql,$info,$pubring,$secring,@recipients,$public,$secret,$data); $info = genKeys($password,$group); if ($info->{keyid}) { $public = $info->{public}; $secret = $info->{secret}; push(@recipients, $info->{keyid}); ($pubring,$secring) = unarmourKeys($public,$secret); $info->{public} = $pubring; $info->{secret} = $secring; $info->{text} = encode_base64($password); $info->{recipients} = \@recipients; $info->{password} = $password; $data = encryptData($group,$info); $sql = "INSERT INTO gpg_group(group_name,fingerprint,public, secret,description,data) VALUES ('$group','$info->{finger}','$public', '$secret','','$data')"; runSQL($sql); } } sub encryptData { my($groups,$info) = @_; my($pgp,$encrypt,$kb,%encArgs,$group); $pgp = initRingsPGP($info->{public},$info->{secret}); $encArgs{Data} = $info->{text}; $encArgs{Recipients} = $info->{recipients}; $encArgs{Armour} = 1; $encrypt = $pgp->encrypt(%encArgs) or die "Error encrypt: " . $pgp +->errstr; $encrypt = uri_escape($encrypt); return($encrypt); } sub initRingsPGP { my($public,$secret) = @_; my($pubring,$secring,%args); $pubring = Crypt::OpenPGP::KeyRing->new( Data => $public ); $secring = Crypt::OpenPGP::KeyRing->new( Data => $secret ); $args{SecRing} = $secring; $args{PubRing} = $pubring; $args{Compat} = COMPAT; $args{Verbosity} = DEBUG; return(Crypt::OpenPGP->new(%args)); } sub encryptGroupData { my($grouppass,$group) = @_; my($text,$info,$encrypt,$sql); $info = setGroupRecipients($group); $info->{text} = encode_base64($grouppass); $encrypt = encryptData($group,$info); $sql = "UPDATE gpg_group SET data='$encrypt' WHERE group_name='$group'"; runSQL($sql); } sub setGroupRecipients { my($group) = @_; my(@recipients,$info,$ring,$public,$uids); $info = getGroupUsersPubkey($group); $public = $info->{public}; $uids = $info->{uids}; $ring = Crypt::OpenPGP::KeyRing->new( Data => $public ); foreach my $uid (@{$uids}) { my $kb = $ring->find_keyblock_by_uid($uid); my $keyid = $kb->key->key_id_hex; push(@recipients, $keyid); } push(@recipients, $info->{keyid} ); $info->{recipients} = \@recipients; return($info); } sub getGroupUsersPubkey { my($group) = @_; my($sql,$sth,$ret,@public,@secret,@uids,$info,$ring,$kb,$keyid,@ke +yids); $sql = "SELECT public,secret FROM gpg_group WHERE group_name='$gro +up'"; $sth = runSQL($sql); $ret = $sth->fetchrow_arrayref; my($pubring,$secring) = unarmourKeys($ret->[0],$ret->[1]); push(@public, $pubring ); push(@secret, $secring ); $ring = Crypt::OpenPGP::KeyRing->new( Data => $pubring ); $kb = $ring->find_keyblock_by_uid($group); $keyid = $kb->key->key_id_hex; $sql = "SELECT u.code,u.public,u.secret FROM gpg_user u, gpg_group_user i, gpg_group g WHERE u.code=i.code AND i.groupid=g.id AND g.group_name='$group'"; $sth = runSQL($sql); while($ret = $sth->fetchrow_arrayref) { my($pub,$sec) = unarmourKeys($ret->[1],$ret->[2]); push(@uids, $ret->[0]); push(@public, $pub ); push(@secret, $sec ); } $info->{keyid} = $keyid; $info->{public} = join("", @public); $info->{secret} = join("", @secret); $info->{uids} = \@uids; return($info); } sub decryptGroupData { my($group,$password) = @_; my($sql,$sth,$ret,$data,$encrypt,%decArgs,$info,@groups); $info = getGroupUsersPubkey($group); $sql = "SELECT data FROM gpg_group WHERE group_name='$group' AND active"; $sth = runSQL($sql); $ret = $sth->fetchrow_arrayref; $info->{data} = $ret->[0]; $info->{pass} = $password; $info->{uid} = $group; my($dec,$valid,$sig) = gpgDecryptData($info); return decode_base64($dec); }
        Found the problem. This sub should be this instead of what it was:
        sub decryptGroupData { my($group,$password,$code) = @_; my($sql,$sth,$ret,$data,$encrypt,%decArgs,$info,@groups,$ring,$kb, +$keyid); $sql = "SELECT data, public, secret FROM gpg_user WHERE code='$code' AND active"; $sth = runSQL($sql); $ret = $sth->fetchrow_arrayref; my($pubring,$secring) = unarmourKeys($ret->[1],$ret->[2]); $ring = Crypt::OpenPGP::KeyRing->new( Data => $pubring ); $kb = $ring->find_keyblock_by_uid($code); $keyid = $kb->key->key_id_hex; $info->{keyid} = $keyid; $info->{public} = $pubring; $info->{secret} = $secring; $info->{uid} = $code; $sql = "SELECT data FROM gpg_group WHERE group_name='$group' AND active"; $sth = runSQL($sql); $ret = $sth->fetchrow_arrayref; $info->{data} = $ret->[0]; $info->{pass} = $password; my($dec,$valid,$sig) = gpgDecryptData($info); return decode_base64($dec); }