pem2openpgp: replace raw numbers with semantic labelling to make it more readable.
[monkeysphere.git] / src / keytrans / pem2openpgp
index 7522c8ff2abc94b296256a7a958eb0ad31c0f770..637eba2a225fecbc399a64e3ad6433461b63a36c 100755 (executable)
@@ -107,6 +107,128 @@ sub fingerprint {
   return Digest::SHA1::sha1(pack('Cn', 0x99, length($rsabody)).$rsabody);
 }
 
+# FIXME: replace the opaque numbers below with
+# semantically-meaningful references based on these tables.
+
+# see RFC 4880 section 9.1 (ignoring deprecated algorithms for now)
+my $asym_algos = { rsa => 1,
+                  elgamal => 16,
+                  dsa => 17,
+                  };
+
+# see RFC 4880 section 9.2
+my $ciphers = { plaintext => 0,
+               idea => 1,
+               tripledes => 2,
+               cast5 => 3,
+               blowfish => 4,
+               aes128 => 7,
+               aes192 => 8,
+               aes256 => 9,
+               twofish => 10,
+             };
+
+# see RFC 4880 section 9.3
+my $zips = { uncompressed => 0,
+            zip => 1,
+            zlib => 2,
+            bzip2 => 3,
+          };
+
+# see RFC 4880 section 9.4
+my $digests = { md5 => 1,
+               sha1 => 2,
+               ripemd160 => 3,
+               sha256 => 8,
+               sha384 => 9,
+               sha512 => 10,
+               sha224 => 11,
+             };
+
+# see RFC 4880 section 5.2.3.21
+my $usage_flags = { certify => 0x01,
+                   sign => 0x02,
+                   encrypt_comms => 0x04,
+                   encrypt_storage => 0x08,
+                   encrypt => 0x0c, ## both comms and storage
+                   split => 0x10, # the private key is split via secret sharing
+                   authenticate => 0x20,
+                   shared => 0x80, # more than one person holds the entire private key
+                 };
+
+# see RFC 4880 section 4.3
+my $packet_types = { pubkey_enc_session => 1,
+                    sig => 2,
+                    symkey_enc_session => 3,
+                    onepass_sig => 4,
+                    seckey => 5,
+                    pubkey => 6,
+                    sec_subkey => 7,
+                    compressed_data => 8,
+                    symenc_data => 9,
+                    marker => 10,
+                    literal => 11,
+                    trust => 12,
+                    uid => 13,
+                    pub_subkey => 14,
+                    uat => 17,
+                    symenc_w_integrity => 18,
+                    mdc => 19,
+                  };
+
+# see RFC 4880 section 5.2.1
+my $sig_types = { binary_doc => 0x00,
+                 text_doc => 0x01,
+                 standalone => 0x02,
+                 generic_certification => 0x10,
+                 persona_certification => 0x11,
+                 casual_certification => 0x12,
+                 positive_certification => 0x13,
+                 subkey_binding => 0x18,
+                 primary_key_binding => 0x19,
+                 key_signature => 0x1f,
+                 key_revocation => 0x20,
+                 subkey_revocation => 0x28,
+                 certification_revocation => 0x30,
+                 timestamp => 0x40,
+                 thirdparty => 0x50,
+               };
+
+
+# see RFC 4880 section 5.2.3.1
+my $subpacket_types = { sig_creation_time => 2,
+                       sig_expiration_time => 3,
+                       exportable => 4,
+                       trust_sig => 5,
+                       regex => 6,
+                       revocable => 7,
+                       key_expiration_time => 9,
+                       preferred_cipher => 11,
+                       revocation_key => 12,
+                       issuer => 16,
+                       notation => 20,
+                       preferred_digest => 21,
+                       preferred_compression => 22,
+                       keyserver_prefs => 23,
+                       preferred_keyserver => 24,
+                       primary_uid => 25,
+                       policy_uri => 26,
+                       usage_flags => 27,
+                       signers_uid => 28,
+                       revocation_reason => 29,
+                       features => 30,
+                       signature_target => 31,
+                       embedded_signature => 32,
+                      };
+
+# bitstring (see RFC 4880 section 5.2.3.24)
+my $features = { mdc => 0x01
+              };
+
+# bitstring (see RFC 4880 5.2.3.17)
+my $keyserver_prefs = { nomodify => 0x80
+                     };
+
 # we're just not dealing with newline business right now.  slurp in
 # the whole file.
 undef $/;
@@ -116,7 +238,10 @@ my $buf = <STDIN>;
 my $rsa = Crypt::OpenSSL::RSA->new_private_key($buf);
 
 $rsa->use_sha1_hash();
-$rsa->use_no_padding();
+
+# see page 22 of RFC 4880 for why i think this is the right padding
+# choice to use:
+$rsa->use_pkcs1_padding();
 
 if (! $rsa->check_key()) {
   die "key does not check";
@@ -124,11 +249,11 @@ if (! $rsa->check_key()) {
 
 my $version = pack('C', 4);
 # strong assertion of identity:
-my $sigtype = pack('C', 0x13);
+my $sigtype = pack('C', $sig_types->{positive_certification});
 # RSA
-my $pubkey_algo = pack('C', 1);
+my $pubkey_algo = pack('C', $asym_algos->{rsa});
 # SHA1
-my $hash_algo = pack('C', 2);
+my $hash_algo = pack('C', $digests->{sha1});
 
 # FIXME: i'm worried about generating a bazillion new OpenPGP
 # certificates from the same key, which could easily happen if you run
@@ -138,35 +263,51 @@ my $hash_algo = pack('C', 2);
 # could an environment variable (if set) override the current time?
 my $timestamp = time();
 
-my $creation_time_packet = pack('CCN', 5, 2, $timestamp);
+my $creation_time_packet = pack('CCN', 5, $subpacket_types->{sig_creation_time}, $timestamp);
 
 
-# usage: signing and certification:
-my $flags = 0x03;
-my $usage_packet = pack('CCC', 2, 27, $flags);
+# FIXME: HARDCODED: what if someone wants to select a different set of
+# usage flags?  For now, we do only authentication.
+my $usage_packet = pack('CCC', 2, $subpacket_types->{usage_flags}, $usage_flags->{authenticate});
 
 
 # FIXME: HARDCODED: how should we determine how far off to set the
 # expiration date?  default is to expire in 2 days, which is insanely
 # short (but good for testing).
 my $expires_in = 86400*2;
-my $expiration_packet = pack('CCN', 5, 9, $expires_in);
+my $expiration_packet = pack('CCN', 5, $subpacket_types->{key_expiration_time}, $expires_in);
 
 
 # prefer AES-256, AES-192, AES-128, CAST5, 3DES:
-my $pref_sym_algos = pack('CCCCCCC', 6, 11, 9, 8, 7, 3, 2);
+my $pref_sym_algos = pack('CCCCCCC', 6, $subpacket_types->{preferred_cipher},
+                         $ciphers->{aes256},
+                         $ciphers->{aes192},
+                         $ciphers->{aes128},
+                         $ciphers->{cast5},
+                         $ciphers->{tripledes}
+                        );
 
 # prefer SHA-1, SHA-256, RIPE-MD/160
-my $pref_hash_algos = pack('CCCCC', 4, 21, 2, 8, 3);
+my $pref_hash_algos = pack('CCCCC', 4, $subpacket_types->{preferred_digest},
+                          $digests->{sha1},
+                          $digests->{sha256},
+                          $digests->{ripemd160}
+                         );
 
 # prefer ZLIB, BZip2, ZIP
-my $pref_zip_algos = pack('CCCCC', 4, 22, 2, 3, 1);
+my $pref_zip_algos = pack('CCCCC', 4, $subpacket_types->{preferred_compression},
+                         $zips->{zlib},
+                         $zips->{bzip2},
+                         $zips->{zip}
+                        );
 
 # we support the MDC feature:
-my $features = pack('CCC', 2, 30, 1);
+my $feature_subpacket = pack('CCC', 2, $subpacket_types->{features},
+                            $features->{mdc});
 
 # keyserver preference: only owner modify (???):
-my $keyserver_pref = pack('CCC', 2, 23, 0x80);
+my $keyserver_pref = pack('CCC', 2, $subpacket_types->{keyserver_prefs},
+                         $keyserver_prefs->{nomodify});
 
 my $subpackets_to_be_hashed =
   $creation_time_packet.
@@ -175,7 +316,7 @@ my $subpackets_to_be_hashed =
   $pref_sym_algos.
   $pref_hash_algos.
   $pref_zip_algos.
-  $features.
+  $feature_subpacket.
   $keyserver_pref;
 
 my $subpacket_octets = pack('n', length($subpackets_to_be_hashed));
@@ -190,8 +331,7 @@ my $sig_data_to_be_hashed =
 
 my $pubkey = make_rsa_pub_key_body($rsa, $timestamp);
 
-#open(KEYFILE, "</home/wt215/gpg-test/key-data");
-my $key_data = make_packet(6, $pubkey);
+my $key_data = make_packet($packet_types->{pubkey}, $pubkey);
 
 # take the last 8 bytes of the fingerprint as the keyid:
 my $keyid = substr(fingerprint($rsa, $timestamp), 20 - 8, 8);
@@ -215,7 +355,7 @@ my $datatosign =
 my $data_hash = Digest::SHA1::sha1_hex($datatosign);
 
 
-my $issuer_packet = pack('CCa8', 9, 16, $keyid);
+my $issuer_packet = pack('CCa8', 9, $subpacket_types->{issuer}, $keyid);
 
 my $sig = Crypt::OpenSSL::Bignum->new_from_bin($rsa->sign($datatosign));
 
@@ -227,8 +367,8 @@ my $sig_body =
   mpi_pack($sig);
 
 print
-  make_packet(6, $pubkey).
-  make_packet(13, $uid).
-  make_packet(2, $sig_body);
+  make_packet($packet_types->{pubkey}, $pubkey).
+  make_packet($packet_types->{uid}, $uid).
+  make_packet($packet_types->{sig}, $sig_body);