Key Generation "file open error" Problem with 1.0.6

Ed Suominen ed@eepatents.com
Wed Dec 26 18:58:02 2001


I am having an intermittent problem (the worst kind to debug!) with GPG 
1.0.6 during batch key generation. First, I generate a "vapor key" 
(destroyed when the crypto session's over) with TCL (see script copied 
below). That step always works, but when I try to --list-keys to get the 
hex ID for the newly generated key, after a 2-second delay, I sometimes 
(20%?) get the following error:

>Keyinfo Result: gpg: c:/tksec/crypto/pubvapor.gpg: keyring created
>gpg: can't open `c:/tksec/crypto/pubvapor.gpg'
>gpg: enum_keyblocks(open) failed: file open error

"keyinfo" is a TCL procedure that creates a list of GPG key emails + hex ID 
codes. (See below.)

My script currently retries the --list-keys operation in hopes of getting 
it done, but it seems that once GPG produces the file open error, it can 
never get over it. I may have to try deleting the new keyring files 
(pubvapor.gpg, secvapor.gpg) on error and redoing the key generation 
command. That would probably help, but I'm uncomfortable with this apparent 
file locking bug with GPG 1.0.6.

Werner, am I doing something wrong in the scripts, or is this a GPG bug 
that can be fixed in 1.0.7?

P.S. - The scripts are part of my free, open-source (but not public domain) 
TKSEC package on SourceForge (http://sf.net/projects/tksec) which I'll be 
announcing here with the first stable release. I don't have time to go into 
the details right now, but briefly, TKSEC uses GPG as a security platform 
for a suite of secure communication tools, all conveniently accessible from 
a single menu, including voice-over-IP, document transfer, and remote 
desktop access. A major benefit is that it uses an FTP command/reply 
channel to get around firewalls, and that the "vapor keys" used for 
encryption are permanently wiped out after the session. The vapor key 
passphrases are pseudorandomly generated and stored only in RAM during the 
session. So, no keyboard sniffer or demand for a key can be used to get at 
your data after you're done with it.

Best regards,

Ed Suominen


#<<<<<<<<<<<< KEYGEN >>>>>>>>>>>>>>#
proc keygen { nameID emailID pass } {

global tempPath cryptoPath

### Create keygen parameter file
###     Key type ElGamal (sign & encrypt) to avoid
###     generating two keys. Key length set to 1024
###     for quick keygen, reasonable security
set fileData [subst {
Key-Type: 20
Key-Length: 1024
Name-Real: aaa
Name-Email: bbb
Expire-Date: 0
Passphrase: ccc
%pubring [file join $cryptoPath pubvapor.gpg]
%secring [file join $cryptoPath secvapor.gpg]
%commit }]

# Substitute provided values for blanks
regsub aaa $fileData $nameID fileData
regsub bbb $fileData $emailID fileData
regsub ccc $fileData $pass fileData
# Write file
set tempSpec1 [file join $tempPath [tempfile keygen.txt]]
set fh [open $tempSpec1 w]
puts $fh $fileData
close $fh

set err1 1; set err2 1
while { $err1 || $err2 } {

         ### Have GnuPG Generate the key
         saymsg "Generating Crypto Key Pair" cmd
         set waitMsg "One Moment: ~~~~~~~~~~~"
         set err1 [catch \
                 {exec [file join $cryptoPath gpg] --batch --gen-key 
$tempSpec1 &} pid]
         # Wait for pid of GPG to go away
         while { [ lsearch [winlist] [list * [format 0x%04x $pid]] ] >= 0 } {
                 saymsg [string range $waitMsg 0 34] cmd
                 regsub {~} $waitMsg + waitMsg
                 # System-dependent delay, hopefully makes steps match time 
per step
                 # Also, exercises hard disk while the key generation is 
using entropy
                 exec fc C:\\tksec\\tcl\\lib\\tcl8.3\\encoding\\*.* \
                         C:\\tksec\\tcl\\lib\\tcl8.3\\encoding\\*.*
         }

         if { $err1 } {
                 saymsg "Key Generation Error" error
         } else {
                 saymsg "Vapor Key Generated" result
                 idle 2
                 set keyID [keyinfo $emailID -vapor]
                 set err2 [catch {exec [file join $cryptoPath gpg] --batch \
                         --no-default-keyring --keyring [file join 
$cryptoPath pubvapor.gpg] \
                         --armor --export $keyID \
                 } key]
         }

         if { $err2 } {
                 saymsg "Key Export Error" error
                 saymsg "Keyinfo Result: $keyID" data
                 saymsg "Export Result: $key" data
         } else {
                 saymsg "Vapor Key Exported" result
         }

### END while loop
}

### Delete tempfiles
catch {file delete $tempSpec1}

### END KEYGEN
return $key
}


#<<<<<<<<<<<< KEYINFO >>>>>>>>>>>>>>#
proc keyinfo { arg {arg2 null} } {

global cryptoPath

### Get raw keylist output
if { [regexp {^-[sS]} $arg2] } {
### -[s]ecret option, use secret keyring
         set err [catch {exec $cryptoPath/gpg --list-secret-keys} keyList]
         if { $err } {
                 set arg error
         } else {
                 set keyList [split $keyList \n]
                 set pat1 {sec\s+[0-9]{3,5}[a-z]\/([a-f0-9]{8})}
         }

} elseif { [regexp {^-[vV]} $arg2] } {
### -[v]apor option, use vapor keyring
         set err [catch {exec $cryptoPath/gpg \
                 --no-default-keyring --keyring [file join $cryptoPath 
pubvapor.gpg] \
                 --list-keys} keyList]
         if { $err } {
                 set arg error
         } else {
                 set keyList [split $keyList \n]
                 set pat1 {pub\s+[0-9]{3,5}[a-z]\/([a-f0-9]{8})}
         }

} else {
### No option set, look for one specified public key
         set err [catch {exec $cryptoPath/gpg --list-keys} keyList]
         if { $err } {
                 set arg error
         } else {
                 set keyList [split $keyList \n]
                 set pat1 {pub\s+[0-9]{3,5}[a-z]\/([a-f0-9]{8})}
         }
}

### Parse keylist
set pat2 {<([a-z0-9@\.]*)>}
switch -regexp -- $arg {
         {error}  { ### KeyList contains error output if error above
                 set result $keyList
         }
         {^-[lL]} { ### -list option, parse all lines for public key listings
                 foreach {i} $keyList {
                         # If this line contains a public key listing,
                         # add the email userID to the result list
                         if { [regexp -nocase $pat1 $i] } {
                                 regexp -nocase $pat2 $i match x
                                 lappend result $x
                         }
                 }
         }
         default { ### Process just one matching line, not -list option
                 ### Caution - case sensitive list search
                 set k [lsearch -regexp $keyList [subst {<$arg>}] ]
                 if { ($k < 0) || \
                 ( [regexp -nocase $pat1 [lindex $keyList \
                         [max 0 $k] ] match result] == 0 ) } {
                                 set result -1
                 }
         }
}

### END KEYINFO
return $result
}