]> git.ipfire.org Git - thirdparty/ntp.git/commitdiff
MV crypto updates from Dave Mills.
authorHarlan Stenn <stenn@ntp.org>
Tue, 26 Nov 2002 04:01:11 +0000 (23:01 -0500)
committerHarlan Stenn <stenn@ntp.org>
Tue, 26 Nov 2002 04:01:11 +0000 (23:01 -0500)
bk: 3de2f2070PZiitojK5dMPAqG8ZVd4w

12 files changed:
html/audio.html
html/authopt.html
html/genkeys.html
html/pic/radio2.jpg [new file with mode: 0644]
include/ntp_config.h
include/ntp_control.h
include/ntp_crypto.h
ntpd/ntp_config.c
ntpd/ntp_control.c
ntpd/ntp_crypto.c
ntpd/ntp_proto.c
util/ntp-keygen.c

index badfe90fa2fcfed7c2fef36eee993fbf42c448f6..f5f1b02749172b8df89ff4fd4631479fe4bfd6b8 100644 (file)
@@ -10,7 +10,7 @@
 
     <body>
         <h3>Reference Clock Audio Drivers</h3>
-        <img src="../../NewFiles/radio2.jpg" alt="gif" align="left">
+        <img src="pic/radio2.jpg" alt="jpg" align="left">
         <h4>Related Links</h4>
         <script type="text/javascript" language="javascript" src="scripts/links8.txt"></script>
         <br clear="left">
index 854c291048b12d06d75a5e5b1fe5db72438b9efb..ae656676af8fe1675254827337260b98696ad6ee 100644 (file)
         <h4 id="#auth">Authentication Support</h4>
         <p>Authentication support allows the NTP client to verify that the server is in fact known and trusted and not an intruder intending accidentally or on purpose to masquerade as that server. The NTPv3 specification RFC-1305 defines a scheme which provides cryptographic authentication of received NTP packets. Originally, this was done using the Data Encryption Standard (DES) algorithm operating in Cipher Block Chaining (CBC) mode, commonly called DES-CBC. Subsequently, this was augmented by the RSA Message Digest 5 (MD5) algorithm using a private key, commonly called keyed-MD5. Either algorithm computes a message digest, or one-way hash, which can be used to verify the server has the correct private key and key identifier.</p>
         <p>NTPv4 retains the NTPv3 scheme, properly described as symmetric key cryptography and, in addition, provides a new Autokey scheme based on public key cryptography. Public key cryptography is generally considered more secure than symmetric key cryptography, since the security is based on a private value which is generated by each server and never revealed. With Autokey all key distribution and management functions involve only public values, which considerably simplifies key distribution and storage. Public key management is based on X.509 certificates, which can be provided by commercial services or produced by utility programs in the OpenSSL software library or the NTPv4 distribution.</p>
-        <p>Authentication is configured separately for each association using the <tt>key</tt> or <tt>autokey</tt> subcommand on the <tt>peer</tt>, <tt>server</tt>, <tt>broadcast</tt> and <tt>manycastclient</tt> configuration commands as described in the <a href="confopt.html">Configuration Options</a> page. The authentication options described below specify the suite of keys, select the key for each configured association and manage the configuration operations. In addition, if public key cryptography is enabled, the commands specify the message digest and signature encryption scheme.</p>
-        <p>Authentication is always enabled, although ineffective if not configured as described below. If a NTP packet arrives including a message authentication code (MAC), only if it passes all cryptographic checks is it accepted for further processing. The checks require correct key ID, key value and message digest. If the packet has been modified in any way or replayed by an intruder, it will fail one or more of these checks and be discarded. Furthermore, the Autokey scheme requires a preliminary protocol exchange to obtain the server public key, verify its credentials and compute a private session key.</p>
-        <p>The <tt>auth</tt> flag controls whether new associations or remote configuration commands require cryptographic authentication. This flag can be set or reset by the <tt>enable</tt> and <tt>disable</tt> commands and also by remote configuration commands sent by a <tt>ntpdc</tt> program running in another machine. If this flag is enabled, which is the default case, new broadcast/manycast client and symmetric passive associations and remote configuration commands must be cryptographically authenticated using either symmetric key or public key cryptography. If this flag is disabled, these operations are effective even if not cryptographic authenticated. It should be understood that operating with the <tt>auth</tt> disabled invites a significant vulnerability where a rogue hacker can seriously disrupt system timekeeping. It is important to note that this flag has no purpose other than to allow or disallow a new association in response to new broadcast and symmetric active messages and remote configuration commands and, in particular, the flag has no effect on the authentication process itself.</p>
-        <p>An attractive alternative where multicast support is available is manycast mode, in which clients periodically troll for servers as described in the <a href="manyopt.html">Automatic NTP Configuration Options</a> page. Either symmetric key or public key cryptographic authentication can be used in this mode. The principle advantage of manycast mode is that potential servers need not be configured in advance, since the client finds them during regular operation, and the configuration files for all clients can be identical.</p>
         <p>While the algorithms for symmetric key cryptography are included in the NTPv4 distribution, public key cryptography requires the OpenSSL software library to be installed before building the NTP distribution. Directions for doing that are on the <a href="build.html">Building and Installing the Distribution</a> page.</p>
+        <p>Authentication is configured separately for each association using the <tt>key</tt> or <tt>autokey</tt> subcommand on the <tt>peer</tt>, <tt>server</tt>, <tt>broadcast</tt> and <tt>manycastclient</tt> configuration commands as described in the <a href="confopt.html">Configuration Options</a> page. The authentication options described below specify the locations of the key files, if other than default, which symmetric keys are trusted and the interval between various operations, if other than default.</p>
+        <p>Authentication is always enabled, although ineffective if not configured as described below. If a NTP packet arrives including a message authentication code (MAC), it is accepted only if it passes all cryptographic checks. The checks require correct key ID, key value and message digest. If the packet has been modified in any way or replayed by an intruder, it will fail one or more of these checks and be discarded. Furthermore, the Autokey scheme requires a preliminary protocol exchange to obtain the server certificate, verify its credentials and initialize the protocol</p>
+        <p>The <tt>auth</tt> flag controls whether new associations or remote configuration commands require cryptographic authentication. This flag can be set or reset by the <tt>enable</tt> and <tt>disable</tt> commands and also by remote configuration commands sent by a <tt>ntpdc</tt> program running in another machine. If this flag is enabled, which is the default case, new broadcast/manycast client and symmetric passive associations and remote configuration commands must be cryptographically authenticated using either symmetric key or public key cryptography. If this flag is disabled, these operations are effective even if not cryptographic authenticated. It should be understood that operating with the <tt>auth</tt> flag disabled invites a significant vulnerability where a rogue hacker can masquerade as a falseticker and seriously disrupt system timekeeping. It is important to note that this flag has no purpose other than to allow or disallow a new association in response to new broadcast and symmetric active messages and remote configuration commands and, in particular, the flag has no effect on the authentication process itself.</p>
+        <p>An attractive alternative where multicast support is available is manycast mode, in which clients periodically troll for servers as described in the <a href="manyopt.html">Automatic NTP Configuration Options</a> page. Either symmetric key or public key cryptographic authentication can be used in this mode. The principle advantage of manycast mode is that potential servers need not be configured in advance, since the client finds them during regular operation, and the configuration files for all clients can be identical.</p>
         <p>The security model and protocol schemes for both symmetric key and public key cryptography are summarized below; further details are in the briefings, papers and reports at the NTP project page linked from <a href="http://www.ntp.org">www.ntp.org</a>.</p>
         <h4 id="#symm">Symmetric Key Cryptography</h4>
         The original RFC-1305 specification allows any one of possibly 65,534 keys, each distinguished by a 32-bit key identifier, to authenticate an association. The servers and clients involved must agree on the key and key identifier to authenticate NTP packets. Keys and related information are specified in a key file, usually called <tt>ntp.keys</tt>, which must be distributed and stored using secure procedures beyond the scope of the NTP protocol itself. Besides the keys used for ordinary NTP associations, additional keys can be used as passwords for the <tt><a href="ntpq.html">ntpq</a></tt> and <tt><a href="ntpdc.html">ntpdc</a></tt> utility programs.
         <p>The specific cryptographic environment used by Autokey servers and clients is determined by a set of files and soft links. This includes a required host key file, required certificate file and optional sign key file, leapsecond file and identity scheme files. The digest/signature scheme is specified in the X.509 certificate along with the matching sign key. There are several schemes available in the OpenSSL software library, each identified by a specific string such as <tt>md5WithRSAEncryption</tt>, which stands for the MD5 message digest with RSA encryption scheme. The current NTP distribution supports all the schemes in the OpenSSL library, including those based on RSA and DSA digital signatures. The schemes currently available are described in the <a href="genkeys.html"><tt>ntp-keygen</tt></a> page.</p>
         <p>The digest/signature scheme and certificate are provided to dependent clients in the Autokey protocol. Different client or peer associations can use different schemes; each of two symmetric peers can use different schemes. Note that the digest/signature scheme is separate and distinct from the NTP message digest used to construct the packet MAC. The only requirement is that the server sign key and signature algorithm must match the public key and verification algorithm specified in the certificate.</p>
         <h4 id="#auto">Autokey Dances</h4>
-        <p>The Autokey protocol is somewhat intricate and a detailed explanation of the subprotocols, called dances, is beyond the scope of this page. Detailed information and briefings can be found via the NTP project page at <a href="html://www.ntp.org">www.ntp.org</a>. Briefly, the dances start when a client requests the server name and status word. Next, the client requests the server certificate containing the public key used to sign subsequent messages. If the issuer of that certificate is not the server, the issuer certificate is requested. This process continues until a trusted, self-signed certificate is obtained.</p>
-        <p>Next comes the optional identity scheme which confirms the server group membership. A group is a clique of client and server hosts conforming to the following:
+        <p>The Autokey protocol is somewhat intricate and a detailed explanation of the subprotocols, called dances, is beyond the scope of this page. Detailed information and briefings can be found on the Autonomous Authentication page cited above. Briefly, the dances start when a client requests the server name and status word. Next, the client requests the server certificate containing the public key used to sign subsequent messages. If the issuer of that certificate is not the server, the issuer certificate is requested. This process continues until a trusted, self-signed certificate is obtained.</p>
+        <p>Next comes the optional identity scheme which confirms the server group membership. A group is a clique of client and server hosts conforming to the following:</p>
         <ol>
-        <li>Each group member holds a private group key generated by a trusted  member of the group.
-        <li>A group member is trusted if it operates at the lowest stratum in the group, has a trusted, self-signed certificate, and has generated the current group key.
-        <li>Clients and servers are configured so that each has an unbroken certificate trail to the trusted member.
+            <li>Each group member holds a private group key generated by a trusted authority.
+            <li>A group member is trusted if it operates at the lowest stratum in the group, has a trusted, self-signed certificate, and has generated the current group key.
+            <li>Clients and servers are configured so that each has an unbroken certificate trail to the trusted member.
         </ol>
-        <p>A host can be a member of two or more groups if the above are satisfied for each group. This provides a mechanism for building multiple interlocking security compartments in the same way that such compartments are built with multiple symmetric keys.
-        <p>There are four schemes to prove identity: one using private certificates (PC), a second using trusted certificates (TC), a third using a modified Schnorr algorithm (IFF aka Identify Friend or Foe) and the fourth using a modified Guillou-Quisquater (GQ) algorithm. The particular scheme and required parameters and certificates are selected using the <tt>ntp-keygen</tt> utility. All but the TC sheme use password-encrypted private values generated by a trusted host and distribted to all other members of the group. The means required to distribute secret passwords are beyond the scope of this page.</p>
-        <p>The PC scheme uses a private certificate as group key. The certificate is generated by a trusted host and distributed to all other group members as a private value. The TC scheme involves a conventional certificate trail and a sequence of certificates, each signed by an issuer one stratum level lower than the client and terminating on a self signed trusted certificate, as described in RFC-2510.</p>
-        <p>The two remaining schemes involve a cryptographically strong challenge-response exchange. Both the IFF and GQ schemes start when the client sends a nonce to the server, which then rolls its own nonce, performs a mathematical operation and sends the results along with a message digest to the client. The client performs a second mathematical operation to produce a digest that must match the one included in the message.</p>
+        <p>A host can be a member of two or more groups if the above are satisfied for each group. This provides a mechanism for building multiple interlocking security compartments in the same way that such compartments are built with multiple symmetric keys.</p>
+        <p>There are five schemes to prove identity: (1) private certificates (PC), (2) trusted certificates (TC), (3) a modified Schnorr algorithm (IFF aka Identify Friend or Foe), (4) a modified Guillou-Quisquater (GQ) algorithm, and (5) a modified Mu-Varadharajan algorithm (MV). The particular scheme and required parameters and certificates are selected using the <tt>ntp-keygen</tt> utility. All but the TC sheme can use password-encrypted private values generated by the trusted authority and distributed to all other members of the group. The means required to distribute secret passwords are beyond the scope of this page.</p>
         <p>The next step in the Autokey dance retrieves the cryptographic values needed in the particular dance, including the cookie, autokey values and leapsecond table. Finally, in all but the PC scheme, the client presents the server with its certificate and asks that it be signed using the server private key. The server response is saved for future dependent clients to hike the certificate trail.</p>
         <h4 id="#inter">Operation</h4>
-        <p>A specific combination of authentication scheme (none, symmetric key, public key) and identity scheme (PC, TC, IFF, GQ) is called a cryptotype, although not all combinations are compatible. There may be management configurations where the clients, servers and peers may not all support the same cryptotypes. A secure NTPv4 subnet can be configured in several ways while keeping in mind the principles explained above and in this section. Note however that some cryptotype combinations may successfully interoperate with each other, but may not represent good security practice.</p>
+        <p>A specific combination of authentication scheme (none, symmetric key, public key) and identity scheme (PC, TC, IFF, GQ, MV) is called a cryptotype, although not all combinations are compatible. There may be management configurations where the clients, servers and peers may not all support the same cryptotypes. A secure NTPv4 subnet can be configured in several ways while keeping in mind the principles explained above and in this section. Note however that some cryptotype combinations may successfully interoperate with each other, but may not represent good security practice.</p>
         <p>The cryptotype of an association is determined at the time of mobilization, either at configuration time or some time later when a message of appropriate cryptotype arrives. When mobilized by a <tt>server</tt> or <tt>peer</tt> configuration command and no <tt>key</tt> or <tt>autokey</tt> subcommands are present, the association is not authenticated; if the <tt>key</tt> subcommand is present, the association is authenticated using the symmetric key ID specified; if the <tt>autokey</tt> subcommand is present, the association is authenticated using Autokey.</p>
-        <p>When multiple identity schemes are supported in the Autokey protocol, the first message exchange determines which one is used. The client request message contains bits corresponding to which schemes it has available. The server response message contains bits corresponding to which schemes it has available. Both server and client match the received bits with their own and select the common scheme judged most secure. For this purpose the order from most secure to least is GC, IFF, TC. Note that PC does not interoperate with any of the rest, since they require the server certificate which a PC server will not reveal.</p>
+        <p>When multiple identity schemes are supported in the Autokey protocol, the first message exchange determines which one is used. The client request message contains bits corresponding to which schemes it has available. The server response message contains bits corresponding to which schemes it has available. Both server and client match the received bits with their own and select the first common scheme found in the order GC, IFF, MV, TC. Note that PC does not interoperate with any of the rest, since they require the server certificate which a PC server will not reveal.</p>
         <p>Following the principle that time is a public value, a server responds to any client packet that matches its cryptotype capabilities. Thus, a server receiving an unauthenticated packet will respond with an unauthenticated packet, while the same server receiving a packet of a cryptotype it supports will respond with packets of that cryptotype. However, unconfigured broadcast or manycast client associations or symmetric passive associations will not be mobilized unless the server supports a cryptotype compatible with the first packet received. By default, unauthenticated associations will not be mobilized unless overridden in a decidedly dangerous way.</p>
         <p>Some examples may help to reduce confusion. Client Alice has no specific cryptotype selected. Server Bob has both a symmetric key file and minimal Autokey files. Alice's unauthenticated messages arrive at Bob, who replies with unauthenticated messages. Cathy has a copy of Bob's symmetric key file and has selected key ID 4 in messages to Bob. Bob verifies the message with his key ID 4. If it's the same key and the message is verified, Bob sends Cathy a reply authenticated with that key. If verification fails, Bob sends Cathy a thing called a crypto-NAK, which tells her something broke. She can see the evidence using the <tt>ntpq</tt> program.</p>
         <p>Denise has rolled her own host key and certificate. She also uses one of the identity schemes as Bob. She sends the first Autokey message to Bob and they both dance the protocol authentication and identity steps. If all comes out okay, Denise and Bob continue as described above.</p>
         <p>It should be clear from the above that Bob can support all the girls at the same time, as long as he has compatible authentication and identity credentials. Now, Bob can act just like the girls in his own choice of servers; he can run multiple configured associations with multiple different servers (or the same server, although that might not be useful). But, wise security policy might preclude some cryptotype combinations; for instance, running an identity scheme with one server and no authentication with another might not be wise.</p>
         <h4 id="#key">Key Management</h4>
-        <p>The cryptographic values used by the Autokey protocol are incorporated as a set of files generated by the <a href="genkeys.html"><tt>ntp-keygen</tt></a> utility program, including symmetric key, required host key and public certificate files, as well as optional sign key, group keys and leapseconds files. Alternatively, host and sign keys and certificate files can be generated by the OpenSSL&nbsp;utilities and certificates can be imported from public certificate authorities. Note that symmetric keys are necessary for the <tt>ntpq</tt> and <tt>ntpdc</tt> utility programs. The remaining files are necessary only for the Autokey protocol. The files incorporate cryptographic values generated by the OpenSSL library algorithms and are in printable PEM-encoded ASCII format. Files containing private values can be password-encrypted. These files are transmitted and stored in encrypted form and decrypted only when loaded by the <tt>ntpd</tt> daemon. Further information about these files and how they are generated and installed is on the <tt>ntp-keygen</tt> page.</p>
+        <p>The cryptographic values used by the Autokey protocol are incorporated as a set of files generated by the <a href="genkeys.html"><tt>ntp-keygen</tt></a> utility program, including symmetric key, host key and public certificate files, as well as sign key, identity parameters and leapseconds files. Alternatively, host and sign keys and certificate files can be generated by the OpenSSL utilities and certificates can be imported from public certificate authorities. Note that symmetric keys are necessary for the <tt>ntpq</tt> and <tt>ntpdc</tt> utility programs. The remaining files are necessary only for the Autokey protocol.</p>
+        <p>These files incorporate cryptographic values generated by the OpenSSL library algorithms and are in printable PEM-encoded ASCII format. Files containing private values can be password-encrypted. These files are transmitted and stored in encrypted form and decrypted only when loaded by the <tt>ntpd</tt> daemon. Further information about these files and how they are generated and installed is on the <tt>ntp-keygen</tt> page.</p>
         <p>Certificates imported from OpenSSL or public certificate authorities have certian limitations. The certificate should be in ASN.1 syntax, X.509 Version 3 format and encoded in PEM, which is the same format used by OpenSSL. The overall length of the certificate encoded in ASN.1 must not exceed 1024 bytes. The subject distinguished name field (<tt>CN</tt>) is the fully qualified name of the host on which it is used; the remaining subject fields are ignored. The certificate extension fields must not contain either a subject key identifier or a issuer key identifier field; however, an extended key usage field for a trusted host must contain the value <tt>trustRoot</tt>;. Other extension fields are ignored.</p>
         <h4 id="#rand">Random Seed File</h4>
         <p>All cryptographically sound key generation schemes must have means to randomize the entropy seed used to initialize the internal pseudo-random number generator used by the library routines. It is important to understand that entropy must be evolved for each generation, for otherwise the random number sequence would be predictable. Various means dependent on external events, such as keystroke intervals, can be used to do this and some systems have built-in entropy sources. Suitable means are described in the OpenSSL software documentation, but are outside the scope of this page.</p>
             <dl>
                 <dt><tt>cert <i>file</i></tt>
                 <dd>Specifies the location of the required host public certificate file. This overrides the link <tt>ntpkey_cert_<i>hostname</i></tt> in the keys directory.
-                <dt><tt>gq <i>file</i></tt>
-                <dd>Specifies the location of the optional GQ keys file. This overrides the link <tt>ntpkey_gq_<i>hostname</i></tt> in the keys directory.
                 <dt><tt>gqpar <i>file</i></tt>
-                <dd>Specifies the location of the optional GQ parameters file. This overrides the link <tt>ntpkey_gqpar_<i>hostname</i></tt> in the keys directory.
+                <dd>Specifies the location of the optional GQ parameters file. This overrides the link <tt>ntpkey_gq_<i>hostname</i></tt> in the keys directory.
                 <dt><tt>host <i>file</i></tt>
                 <dd>Specifies the location of the required host key file. This overrides the link <tt>ntpkey_key_<i>hostname</i></tt> in the keys directory.
-                <dt><tt>iff <i>file</i></tt>
+                <dt><tt>iffpar <i>file</i></tt>
                 <dd>Specifies the location of the optional IFF parameters file.This overrides the link <tt>ntpkey_iff_<i>hostname</i></tt> in the keys directory.
                 <dt><tt>leap <i>file</i></tt>
                 <dd>Specifies the location of the optional leapsecond file. This overrides the link <tt>ntpkey_leap</tt> in the keys directory.
+                <dt><tt>mvpar <i>file</i></tt>
+                <dd>Specifies the location of the optional MV parameters file. This overrides the link <tt>ntpkey_mv_<i>hostname</i></tt> in the keys directory.
                 <dt><tt>pw <i>password</i></tt>
                 <dd>Specifies the password to decrypt files containing private keys and identity parameters. This is required only if these files have been encrypted.
                 <dt><tt>randfile <i>file</i></tt>
index 29a45a2e6c5e4db88b4c5c2107d2ada9aa593b6c..d0d929e6dbbe2349f5e81f396c6ce2800148661f 100644 (file)
@@ -20,7 +20,8 @@
             <li class="inline"><a href="#synop">Synopsis</a>
             <li class="inline"><a href="#desc">Description</a>
             <li class="inline"><a href="#run">Running the program</a>
-            <li class="inline"><a href="#exam">Examples</a>
+            <li class="inline"><a href="#exam">Trusted Hosts and Groups</a>
+            <li class="inline"><a href="#idexp">Identity Schemes</a>
             <li class="inline"><a href="#cmd">Command Line Options</a>
             <li class="inline"><a href="#symkey">Symmetric Key File</a>
             <li class="inline"><a href="#priv">Private Key Files</a>
@@ -35,9 +36,9 @@
         <tt>ntp-keygen</tt>
         <h4 id="#desc">Description</h4>
         <p>This program generates cryptographic data files used by the NTPv4 authentication and identification schemes. It generates MD5 key files used in symmetric key cryptography. In addition, if the OpenSSL software library has been installed, it generates key, certificate and parameter files used in public key cryptography. These files are used for cookie encryption, digital signature and challenge/response identification algorithms compatible with the Internet standard security infrastructure.</p>
-        <p>All files are in PEM-encoded printable ASCII format, so they can be embedded as MIME attachments in mail to other sites and certificate authorities. Files containing private values can be password encrypted. File names begin with the prefix <tt>ntpkey_</tt> and end with the postfix <tt><i>hostname.filestamp</i></tt>, where <tt><i>hostname</i></tt> is the owner name, usually the string returned by the Unix <tt>gethostname()</tt> routine, and <tt><i>filestamp</i></tt> is the NTP seconds when the file was generated, in decimal digits. This both guarantees uniqueness and simplifies maintenance procedures, since all files can be quickly removed by a <tt>rm ntpkey*</tt> command or all files generated at a specific time can be removed by a <tt>rm *<i>filestamp</i></tt> command. To further reduce the risk of misconfiguration, the first two lines of a file contain the file name and generation date and time as comments.</p>
+        <p>All files are in PEM-encoded printable ASCII format, so they can be embedded as MIME attachments in mail to other sites and certificate authorities. Files containing private values can be password encrypted. File names begin with the prefix <tt>ntpkey_</tt> and end with the postfix <tt><i>_hostname.filestamp</i></tt>, where <tt><i>hostname</i></tt> is the owner name, usually the string returned by the Unix <tt>gethostname()</tt> routine, and <tt><i>filestamp</i></tt> is the NTP seconds when the file was generated, in decimal digits. This both guarantees uniqueness and simplifies maintenance procedures, since all files can be quickly removed by a <tt>rm ntpkey*</tt> command or all files generated at a specific time can be removed by a <tt>rm *<i>filestamp</i></tt> command. To further reduce the risk of misconfiguration, the first two lines of a file contain the file name and generation date and time as comments.</p>
         <p>All files are installed by default in the keys directory <tt>/usr/local/etc</tt>, which is normally in a shared filesystem in NFS-mounted networks. The actual location of the keys directory or each file can be overridden by configuration commands, but this is not recommended. Normally, the files for each host are generated by that host and used only by that host, although exceptions exist as noted later on this page.</p>
-        <p>Normally, files containing private values, including the host key, sign key and identification parameters, are permitted root read/write-only; while others containing public values are permitted world readable. Alternatively, files containing private values can be encrypted with a chosen password and all files permitted world readable, which simplifies maintenance in shared file systems. Since uniqueness is insured by the hostname and file name extensions, the files for a NFS server and dependent clients can all be installed in the same directory.</p>
+        <p>Normally, files containing private values, including the host key, sign key and identification parameters, are permitted root read/write-only; while others containing public values are permitted world readable. Alternatively, files containing private values can be encrypted with a chosen password and all files permitted world readable, which simplifies maintenance in shared file systems. Since uniqueness is insured by the hostname and file name extensions, the files for a NFS server and dependent clients can all be installed in the same shared directory.</p>
         <p>The recommended practice is to keep the file name extensions when installing a file and to install a soft link from the generic names specified elsewhere on this page to the generated files. This allows new file generations to be activated simply by changing the link. However, <tt>ntpd</tt> parses the link name when present to extract the filestamp and sends it along with the public key and host name when requested. This allows clients to verify that the file and generation times are always current. The <tt>ntp-keygen</tt> program uses the same timestamp extension for all files generated at one time, so each generation is distinct and can be readily recognized in monitoring data.</p>
         <p>As explained in the <a href="authopt.html">Authentication Options</a> page, all cryptographically sound key generation schemes must have means to randomize the entropy seed used to initialize the internal random number generator. The OpenSSL library uses a designated random seed file for this purpose. The file must be available when starting the NTP daemon and <tt>ntp-keygen</tt> program. If a site supports OpenSSL or its companion OpenSSH, it is very likely that means to do this are already available.</p>
         <h4 id="#run">Running the program</h4>
         <p>Running the program as other than root and using the Unix <tt>su</tt> command to assume root may not work properly, since by default the OpenSSL library looks for the random seed file <tt>.rnd</tt> in the user home directory. However, there should be only one <tt>.rnd</tt>, most conveniently in the root directory, so it is convenient to define the <tt>$RANDFILE</tt> environment variable used by the OpenSSL library as the path to <tt>/.rnd</tt>.</p>
         <p>Installing the keys as root might not work in NFS-mounted shared file systems, as NFS clients may not be able to write to the shared keys directory, even as root. In this case, NFS clients can specify the files in another directory such as <tt>/etc</tt> using the <tt>keysdir</tt> command. There is no need for one client to read the keys and certificates of other clients or servers, as these data are obtained automatically by the Autokey protocol.</p>
         <p>Ordinarily, cryptographic files are generated by the host that uses them, but it is possible for a trusted host to generate these files for other hosts; however, in such cases the private values should always be password-encrypted. The password is supplied as an option to the <tt>ntp-keygen</tt> command and as the <tt>pw</tt> subcommand in the <tt>ntpd</tt> cofiguration file. It is convenient to designate the owner name and trusted name as the subject and issuer fields, respectivily, of the certificate. The owner name is also used for the host and sign key files, while the trusted name is used for the identity files.</p>
-        <h4 id="#exam">Examples</h4>
+        <h4 id="#exam">Trusted Hosts and Groups</h4>
         <p>Each cryptographic configuration involves selection of a signature scheme and identification scheme, called a cryptotype, as explained in the <a href="authopt.html">Authentication Options</a> page. The default cryptotype uses RSA encryption, MD5 message digest and default (TC) identification. First, configure a NTP subnet including a number of low-stratum time servers from which all other subnet members derive synchronization directly or indirectly. These servers are considered trusted and will have trusted certificates. All others will automatically and dynamically build authoritative certificate trails to these servers.</p>
-        <p>On each trusted server as root, change to the keys directory. To insure a fresh fileset, remove all <tt>ntpkey</tt> files. Then run <tt>ntp-keygen -T</tt> to generate keys and a trusted certificate for the TC identification scheme. On all other hosts do the same, but leave off the <tt>-T</tt> flag to generate keys and nontrusted certificates. When complete, start <tt>ntpd</tt> starting from the lowest stratum and working up the tree. It may take some time for Autokey to instantiate the certificate trails throughout the subnet, but setting up the environment is completely automatic.</p>
+        <p>On each trusted server as root, change to the keys directory. To insure a fresh fileset, remove all <tt>ntpkey</tt> files. Then run <tt>ntp-keygen -T</tt> to generate keys and a trusted certificate. On all other hosts do the same, but leave off the <tt>-T</tt> flag to generate keys and nontrusted certificates. When complete, start the NTP daemons beginning at the lowest stratum and working up the tree. It may take some time for Autokey to instantiate the certificate trails throughout the subnet, but setting up the environment is completely automatic.</p>
+<p>It is important that every group be able to construct a certificate trail to one or more trusted hosts in the same group. Ordinarily, each host on the trail to a trusted host has certificates for all hosts along the trail, so a host can hike the trail by fetching each in turn until a trusted host is found. This requires all NTP configuration files to specify servers known to be in the group. If the certificate trail is broken so that no trusted host can be found, a host will loop forever (at low rate) until a path comes available.
+        <p>A host can be a member of more than one group if it has the necessary credentials. It might be the case, for example, that the servers in one corporation be members of the corporate group, as well as members of a national standards group from which it imports time. In such cases a host exports its home group when operating as a server and imports the group of each association separately when operating as a client. For instance, server alice exports the her own credentials to dependent clients, but  imports the credentials from the certificate trail of each client association specific to that association.</p>
         <p>After setting up the environment it is advisable to update certificates from time to time, if only to extend the validity interval. Simply run <tt>ntp-keygen</tt> with the same flags as before to generate new certificates using existing keys. The next time <tt>ntpd</tt> is started, it will load the new certificate and restart the protocol. Since the keys have not changed, other dependent hosts will continue as usual until signatures are refreshed and the protocol is restarted, which occurs about once per day.</p>
-        <p>As mentioned on the Authentication Options page, the TC identity scheme is vulnerable to a middleman attack. However, there are more secure identity schemes available, including PC, IFF and GQ. These schemes are based on a trusted host and a group of hosts deriving trust from that host. Group membership is defined by private values generated by the trusted host and distributed by secure means to all other hosts in the group.</p>
-        <p>The PC scheme is set up as follows. On trusted host <i>alice</i> run <tt>ntp-keygen -P -p <i>password</i></tt> to generate the encrypted host key and private certificate. Note that, as with all encrypted files, the <tt>ntp-keygen</tt> program prompts for the password if it reads an encrypted file and the <tt>-p</tt> option is not present.</p>
-        <p>Copy the host key file <tt>ntpkey_RSAkey_<i>alice.filestamp</i></tt> and certificate file <tt>ntpkey_RSA-MD5_cert_<i>alice.filestamp</i></tt> to all other hosts in the group. On each host <i>bob</i> install a soft link from the generic name <tt>ntpkey_host_bob</tt> to the host key file and soft link <tt>ntpkey_cert_bob</tt> to the certificate file. Note the generic links are from host <i>bob</i>, but point to files generated by trusted host <i>alice</i>. Note that it is not possible to change either the keys or certificates in any host without refreshing all other hosts in the group.</p>
-        <p>For the IFF scheme proceed as in the TC scheme to generate keys and certificates for all hosts, including the trusted host. On the trusted host use the <tt>-I -p <i>password</i></tt> options. Then, copy the IFF parameters file <tt>ntpkeys_IFFpar_<i>alice.filestamp</i></tt> to all other hosts in the group. On each host <i>bob</i> install a soft link from the generic <tt>ntpkey_iffpar_<i>alice</i></tt> to this file and a soft link from the generic <tt>ntpkey_iffpar_<i>bob</i></tt> to the same file. As the IFF scheme is independent of keys and certificates, these files can be regenerated as needed.</p>
-        <p>For the GQ scheme proceed as in the TC scheme to generate keys and certificates for all hosts, including the trusted host. On the trusted host use the <tt>-G -p <i>password</i></tt> options. Then, copy the GQ parameters file <tt>ntpkeys_GQpar_<i>alice.filestamp</i></tt> to all other hosts in the group. On each host <i>bob</i> install a soft link from the generic <tt>ntpkey_gqpar_<i>alice</i></tt> to this file and a soft link from the generic <tt>ntpkey_gqpar_<i>bob</i></tt> to the same file. If the <tt>ntp-keygen</tt> program is run on <i>bob</i> before these steps are complete, then run the program again on to generate the GQ keys file <tt>ntpkeys_GQkey_bob</tt> and link. As the GQ scheme generates both the GQ key file and certificate at the same time, but leaving the GQ parameter file intact, keys and certificates can be regenerated as needed.</p>
+<h4 id="#idexp">Identity Schemes</h4>
+
+        <p>As mentioned on the Authentication Options page, the default TC identity scheme is vulnerable to a middleman attack. However, there are more secure identity schemes available, including PC, IFF, GQ and MV. These schemes are based on a trusted agent host and a group of hosts deriving trust from that host. The trusted agent is not necessarily one of the group hosts, bout often is. Group membership is defined by private values generated by the trusted agent and distributed by secure means to all group hosts.</p>
+        <p>The PC scheme is set up as follows. On trusted agent <i>alice</i> run <tt>ntp-keygen -P -p <i>password</i></tt> to generate the encrypted host key and private certificate. Do not generate keys and certificates on the other group hosts. Note that, as with all encrypted files, the <tt>ntp-keygen</tt> program prompts for the password if it reads an encrypted file and the <tt>-p</tt> option is not present.</p>
+        <p>Copy the host key file <tt>ntpkey_RSAkey_<i>alice.filestamp</i></tt> and certificate file <tt>ntpkey_RSA-MD5_cert_<i>alice.filestamp</i></tt> to all group hosts. On each host <i>bob</i> install a soft link from the generic name <tt>ntpkey_host_bob</tt> to the host key file and soft link <tt>ntpkey_cert_bob</tt> to the certificate file. Note the generic links are on <i>bob</i>, but point to files generated by <i>alice</i>. In this scheme it is not possible to refresh either the keys or certificates in any host without copying them to all other hosts in the group.</p>
+        <p>For the IFF scheme and trusted agent <i>Alice</i>, run the <tt>ntp-keygen</tt> program with the <tt>-I -p <i>password</i></tt> options. Then proceed as in the TC scheme to generate keys and certificates for each group host. Then, copy the IFF parameters file <tt>ntpkeys_IFFpar_<i>alice.filestamp</i></tt> to all hosts in the group. On each host <i>bob</i> install a soft link from the generic <tt>ntpkey_iff_<i>alice</i></tt> to this file and a soft link from the generic <tt>ntpkey_iff_<i>bob</i></tt> to the same file. As the IFF scheme is independent of keys and certificates, these files can be refreshed as needed.</p>
+        <p>For the GQ scheme and trusted agent <i>Alice</i>, run the <tt>ntp-keygen</tt> program with the <tt>-G -p <i>password</i></tt> options. Then proceed as in the TC scheme to generate keys and certificates for each group host. Then, copy the GQ parameters file <tt>ntpkeys_GQpar_<i>alice.filestamp</i></tt> to all group hosts. On each host <i>bob</i> install a soft link from the generic <tt>ntpkey_gq_<i>alice</i></tt> to this file and a soft link from the generic <tt>ntpkey_gq_<i>bob</i></tt> to the same file. As the GQ scheme updates the GQ parameters file and certificate at the same time, keys and certificates can be regenerated as needed.</p>
+        <p>In the MV scheme the trusted agent generates server parameters, which are copied to all group hosts restricted to operate only as servers, and client parameters, which are copied to all group hosts restricted to operate only as clients. On trusted agent <i>Alice</i> run the <tt>ntp-keygen</tt> program with the <tt>-V&nbsp;<i>n</i> -p <i>password</i></tt> options, where <i>n</i> is the number of revokable keys, typically 5. Then, copy the MV parameters file <tt>ntpkeys_MVpar_<i>alice.filestamp</i></tt> to all group servers. On server <i>bob</i> install a soft link from the generic <tt>ntpkey_mv_<i>bob</i></tt> to this file. Select one of the <tt>ntpkeys_MVkey<i>k</i>_<i>alice.filestamp</i></tt> files (except the last)where <i><tt>k</tt></i> is the key number, and copy it to all group clients, including those servers that may become clients of other servers.  On client <i>dave</i> install a soft link from the generic <tt>ntpkey_mvkey_<i>alice</i></tt> to this file. As the MV scheme is independent of keys and certificates, these files can be refreshed as needed.</p>
         <p>If it is necessary to use a different sign key or different digest/signature scheme than the default, run <tt>ntp-keygen</tt> with the <tt>-S</tt> option and either <tt>RSA</tt> or <tt>DSA</tt> argument as needed. The most often need to do this is when a DSA-signed certificate is used. If <tt>ntp-keygen</tt> is run again without the option, it will generate a new certificate using the existing sign key. If it is necessary to use a different certificate scheme than the default, run <tt>ntp-keygen</tt> with the <tt>-c</tt> option and selected scheme as needed. If <tt>ntp-keygen</tt> is run again without the option, it will generate a new certificate using the same scheme and existing sign key.</p>
         <h4 id="#cmd">Command Line Options</h4>
         <dl>
diff --git a/html/pic/radio2.jpg b/html/pic/radio2.jpg
new file mode 100644 (file)
index 0000000..ceb7c76
Binary files /dev/null and b/html/pic/radio2.jpg differ
index 0e56dd87ac8510b9a18795ece8ff6265aa32a3bb..765fb8ce02f5a61d1acdca877fcf35d5b1047a37 100644 (file)
 #define CONF_CRYPTO_CERT       4
 #define CONF_CRYPTO_RAND       5
 #define CONF_CRYPTO_KEYS       6
-#define        CONF_CRYPTO_IFF         7
+#define        CONF_CRYPTO_IFFPAR      7
 #define CONF_CRYPTO_GQPAR      8
-#define CONF_CRYPTO_GQ         9
+#define CONF_CRYPTO_MVPAR      9
 #define CONF_CRYPTO_PW         10
 #endif /* OPENSSL */
 
index 3bc14ceab89963ea6399fe3b2eb581e44f9bf64c..0fc726f65330df759d053789d8f9697bcd133d2a 100644 (file)
@@ -225,7 +225,8 @@ struct ntp_control {
 #define        CP_INITKEY      41
 #define        CP_INITTSP      42
 #define        CP_DIGEST       43
-#define        CP_MAXCODE      CP_DIGEST
+#define CP_IDENT       44
+#define        CP_MAXCODE      CP_IDENT
 #else
 #define        CP_MAXCODE      CP_VARLIST
 #endif /* OPENSSL */
index 578e34a77664e9bcbf86db52cf78528932e695a0..c21661834371a3e1765db5971668f0b961726d9a 100644 (file)
@@ -13,6 +13,7 @@
 #define CRYPTO_FLAG_PRIV  0x0010 /* PC identity scheme */
 #define CRYPTO_FLAG_IFF   0x0020 /* IFF identity scheme */
 #define CRYPTO_FLAG_GQ   0x0040 /* GQ identity scheme */
+#define        CRYPTO_FLAG_MV    0x0080 /* MV identity scheme */
 #define CRYPTO_FLAG_MASK  0x00f0 /* identity scheme mask */
        
 /*
@@ -50,6 +51,7 @@
 #define        CRYPTO_SIGN     CRYPTO_CMD(6) /* certificate sign */
 #define CRYPTO_IFF     CRYPTO_CMD(7) /* IFF identity scheme */
 #define CRYPTO_GQ      CRYPTO_CMD(8) /* GQ identity scheme */
+#define        CRYPTO_MV       CRYPTO_CMD(9) /* MV identity scheme */
 #define CRYPTO_RESP    0x80000000 /* response */
 #define CRYPTO_ERROR   0x40000000 /* error */
 
 #define CRYPTO_CONF_CERT  5    /* certificate file name */
 #define CRYPTO_CONF_RAND  6    /* random seed file name */
 #define        CRYPTO_CONF_TRST  7     /* specify trust */
-#define CRYPTO_CONF_IFF   8    /* IFF parameters file name */
+#define CRYPTO_CONF_IFFPAR 8   /* IFF parameters file name */
 #define CRYPTO_CONF_GQPAR 9    /* GQ parameters file name */
-#define CRYPTO_CONF_GQ  10     /* GQ keys file name */
-#define CRYPTO_CONF_PW  11     /* private key password */
+#define        CRYPTO_CONF_MVPAR 10    /* GQ parameters file name */
+#define CRYPTO_CONF_PW   11    /* private key password */
 
 /*
  * Miscellaneous crypto stuff
index 5554f0e5e3420abe12cb4ecdbcb777ef114a99a8..fe585784fe30d70d67dd5c29fb9f7b9cd0ff5450 100644 (file)
@@ -279,11 +279,11 @@ static struct keyword tos_keywords[] = {
  */
 static struct keyword crypto_keywords[] = {
        { "cert",               CONF_CRYPTO_CERT },
-       { "gq",                 CONF_CRYPTO_GQ },
        { "gqpar",              CONF_CRYPTO_GQPAR },
        { "host",               CONF_CRYPTO_RSA },
-       { "iff",                CONF_CRYPTO_IFF },
+       { "iffpar",             CONF_CRYPTO_IFFPAR },
        { "leap",               CONF_CRYPTO_LEAP },
+       { "mvpar",              CONF_CRYPTO_MVPAR },
        { "pw",                 CONF_CRYPTO_PW },
        { "randfile",           CONF_CRYPTO_RAND },
        { "sign",               CONF_CRYPTO_SIGN },
@@ -1141,16 +1141,16 @@ getconfig(
                                crypto_config(CRYPTO_CONF_PRIV, tokens[i]);
                                break;
 
-                           case CONF_CRYPTO_IFF:
-                               crypto_config(CRYPTO_CONF_IFF, tokens[i]);
+                           case CONF_CRYPTO_IFFPAR:
+                               crypto_config(CRYPTO_CONF_IFFPAR, tokens[i]);
                                break;
 
                            case CONF_CRYPTO_GQPAR:
                                crypto_config(CRYPTO_CONF_GQPAR, tokens[i]);
                                break;
 
-                           case CONF_CRYPTO_GQ:
-                               crypto_config(CRYPTO_CONF_GQ, tokens[i]);
+                           case CONF_CRYPTO_MVPAR:
+                               crypto_config(CRYPTO_CONF_MVPAR, tokens[i]);
                                break;
 
                            case CONF_CRYPTO_LEAP:
index ccbda847e489aa82a0c4153484c48fd30b86883d..977ec4c70e1d6bf51e9af464f285cc679079ad16 100644 (file)
@@ -210,6 +210,7 @@ static struct ctl_var peer_var[] = {
        { CP_INITKEY,   RO, "initkey" },        /* 41 */
        { CP_INITTSP,   RO, "timestamp" },      /* 42 */
        { CP_DIGEST,    RO, "signature" },      /* 43 */
+       { CP_IDENT,     RO, "identity" },       /* 44 */
 #endif /* OPENSSL */
        { 0,            EOV, "" }               /* 38/43 */
 };
@@ -253,6 +254,7 @@ static u_char def_peer_var[] = {
        CP_HOST,
        CP_DIGEST,
        CP_FLAGS,
+       CP_IDENT,
        CP_INITSEQ,
 #endif /* OPENSSL */
        0
@@ -1640,6 +1642,12 @@ ctl_putpeer(
                            strlen(peer->subject));
                break;
 
+       case CP_IDENT:
+               if (peer->issuer != NULL)
+                       ctl_putstr(peer_var[CP_IDENT].text, peer->issuer,
+                           strlen(peer->issuer));
+               break;
+
        case CP_INITSEQ:
                if ((ap = (struct autokey *)peer->recval.ptr) == NULL)
                        break;
index 63c51745ca6e9525446be23c62d3bbec4e39710b..0ddd47679337ad9240cf3226b083304a6d2d52cb 100644 (file)
@@ -94,8 +94,6 @@
 #define VALUE_LEN      (6 * 4) /* min response field length */
 #define YEAR           (60 * 60 * 24 * 365) /* seconds in year */
 #define NTP_RANDFILE   "/.rnd" /* OpenSSL random seed file */
-#define TYPE_PRIVATE   1       /* PEM private key */
-#define TYPE_PUBLIC    2       /* PEM public key */
 
 /*
  * Global cryptodata in host byte order
@@ -117,17 +115,17 @@ struct value tai_leap;            /* leapseconds table */
 static char *passwd = NULL;    /* private key password */
 static EVP_PKEY *host_pkey = NULL; /* host key */
 static EVP_PKEY *sign_pkey = NULL; /* sign key */
-static EVP_PKEY *iff_pkey = NULL; /* IFF key */
-static EVP_PKEY        *gqpar_pkey = NULL; /* GQ parmeters */
-static EVP_PKEY        *gq_pkey = NULL; /* GQ parmeters */
+static EVP_PKEY *iffpar_pkey = NULL; /* IFF parameters */
+static EVP_PKEY        *gqpar_pkey = NULL; /* GQ parameters */
+static EVP_PKEY        *mvpar_pkey = NULL; /* MV parameters */
 static const EVP_MD *sign_digest = NULL; /* sign digest */
 static u_int sign_siglen;      /* sign key length */
 static char *rand_file = NTP_RANDFILE; /* random seed file */
 static char *host_file = NULL; /* host key file */
 static char *sign_file = NULL; /* sign key file */
-static char *iff_file = NULL;  /* IFF key file */
+static char *iffpar_file = NULL; /* IFF parameters file */
 static char *gqpar_file = NULL;        /* GQ parameters file */
-static char *gq_file = NULL;   /* GQ key file */
+static char *mvpar_file = NULL;        /* MV parameters file */
 static char *cert_file = NULL; /* certificate file */
 static char *leap_file = NULL; /* leapseconds file */
 
@@ -140,10 +138,13 @@ static    int     crypto_encrypt  P((struct exten *, struct value *,
                                    keyid_t *));
 static int     crypto_alice    P((struct peer *, struct value *));
 static int     crypto_alice2   P((struct peer *, struct value *));
+static int     crypto_alice3   P((struct peer *, struct value *));
 static int     crypto_bob      P((struct exten *, struct value *));
 static int     crypto_bob2     P((struct exten *, struct value *));
+static int     crypto_bob3     P((struct exten *, struct value *));
 static int     crypto_iff      P((struct exten *, struct peer *));
 static int     crypto_gq       P((struct exten *, struct peer *));
+static int     crypto_mv       P((struct exten *, struct peer *));
 static u_int   crypto_send     P((struct exten *, struct value *));
 static tstamp_t crypto_time    P((void));
 static u_long  asn2ntp         P((ASN1_TIME *));
@@ -152,7 +153,7 @@ static      int     cert_sign       P((struct exten *, struct value *));
 static int     cert_valid      P((struct cert_info *, EVP_PKEY *));
 static int     cert_install    P((struct exten *, struct peer *));
 static void    cert_free       P((struct cert_info *));
-static EVP_PKEY *crypto_key    P((char *, tstamp_t *, int));
+static EVP_PKEY *crypto_key    P((char *, tstamp_t *));
 static int     bighash         P((BIGNUM *, BIGNUM *));
 static struct cert_info *crypto_cert P((char *));
 static void    crypto_tai      P((char *));
@@ -699,6 +700,46 @@ crypto_recv(
                                printf("crypto_recv: %s\n", statstr);
 #endif
                        break;
+
+               /*
+                * MV
+                */
+               case CRYPTO_MV | CRYPTO_RESP:
+
+                       /*
+                        * Discard the message if invalid or identity
+                        * already confirmed.
+                        */
+                       if (peer->crypto & CRYPTO_FLAG_VRFY)
+                               break;
+
+                       if ((rval = crypto_verify(ep, NULL, peer)) !=
+                           XEVNT_OK)
+                               break;
+
+                       /*
+                        * If the the challenge matches the response,
+                        * the certificate public key, as well as the
+                        * server public key, signatyre and identity are
+                        * all verified at the same time. The server is
+                        * declared trusted, so we skip further
+                        * certificate stages and move immediately to
+                        * the cookie stage.
+                        */
+                       if ((rval = crypto_mv(ep, peer)) != XEVNT_OK)
+                               break;
+
+                       peer->crypto |= CRYPTO_FLAG_VRFY |
+                           CRYPTO_FLAG_PROV;
+                       peer->flash &= ~TEST10;
+                       sprintf(statstr, "mv fs %u",
+                           ntohl(ep->fstamp));
+                       record_crypto_stats(&peer->srcadr, statstr);
+#ifdef DEBUG
+                       if (debug)
+                               printf("crypto_recv: %s\n", statstr);
+#endif
+                       break;
        
                /*
                 * X509 certificate sign response. Validate the
@@ -1022,6 +1063,7 @@ crypto_recv(
                 */
                case CRYPTO_IFF:
                case CRYPTO_GQ:
+               case CRYPTO_MV:
                case CRYPTO_SIGN:
                        if (len < VALUE_LEN) {
                                rval = XEVNT_LEN;
@@ -1229,6 +1271,28 @@ crypto_xmit(
                value_free(&vtemp);
                break;
 
+       /*
+        * Send challenge in MV identity scheme.
+        */
+       case CRYPTO_MV:
+               if ((peer = findpeerbyassoc(ep->pkt[0])) == NULL) {
+                       opcode |= CRYPTO_ERROR;
+                       break;
+               }
+               if ((rval = crypto_alice3(peer, &vtemp)) == XEVNT_OK)
+                       len += crypto_send(fp, &vtemp);
+               value_free(&vtemp);
+               break;
+
+       /*
+        * Send response in MV identity scheme.
+        */
+       case CRYPTO_MV | CRYPTO_RESP:
+               if ((rval = crypto_bob3(ep, &vtemp)) == XEVNT_OK)
+                       len += crypto_send(fp, &vtemp);
+               value_free(&vtemp);
+               break;
+
        /*
         * Send certificate sign response. The integrity of the request
         * certificate has already been verified on the receive side.
@@ -1897,7 +1961,7 @@ crypto_alice(
        struct value *vp        /* value pointer */
        )
 {
-       DSA     *dsa;           /* IFF key */
+       DSA     *dsa;           /* IFF parameters */
        BN_CTX  *bctx;          /* BIGNUM context */
        EVP_MD_CTX ctx;         /* signature context */
        char    filename[MAXFILENAME + 1];
@@ -1917,7 +1981,7 @@ crypto_alice(
        if (peer->ident_pkey != NULL)
                EVP_PKEY_free(peer->ident_pkey);
        snprintf(filename, MAXFILENAME, "ntpkey_iff_%s", peer->issuer);
-       peer->ident_pkey = crypto_key(filename, &fstamp, TYPE_PRIVATE);
+       peer->ident_pkey = crypto_key(filename, &fstamp);
        if (peer->ident_pkey == NULL) {
                msyslog(LOG_ERR,
                    "crypto_alice: file %s not found or corrupt",
@@ -1978,12 +2042,13 @@ crypto_bob(
        struct value *vp        /* value pointer */
        )
 {
-       DSA     *dsa;           /* IFF key */
+       DSA     *dsa;           /* IFF parameters */
        DSA_SIG *sdsa;          /* DSA signature context fake */
        BN_CTX  *bctx;          /* BIGNUM context */
        EVP_MD_CTX ctx;         /* signature context */
        tstamp_t tstamp;        /* NTP timestamp */
        BIGNUM  *bn, *bk, *r;
+       u_char  *ptr;
        u_int   len;
 
        /*
@@ -1994,7 +2059,7 @@ crypto_bob(
                msyslog(LOG_ERR, "crypto_bob: IFF unavailable");
                return (XEVNT_PUB);
        }
-       dsa = iff_pkey->pkey.dsa;
+       dsa = iffpar_pkey->pkey.dsa;
 
        /*
         * Extract r from the challenge.
@@ -2038,8 +2103,9 @@ crypto_bob(
                return (XEVNT_PUB);
        }
        vp->vallen = htonl(len);
-       vp->ptr = emalloc(len);
-       i2d_DSA_SIG(sdsa, (u_char **)&vp->ptr);
+       ptr = emalloc(len);
+       vp->ptr = ptr;
+       i2d_DSA_SIG(sdsa, &ptr);
        DSA_SIG_free(sdsa);
        vp->siglen = 0;
        if (tstamp == 0)
@@ -2068,9 +2134,9 @@ crypto_iff(
        struct peer *peer       /* peer structure pointer */
        )
 {
-       DSA     *dsa;           /* IFF key */
+       DSA     *dsa;           /* IFF parameters */
        BN_CTX  *bctx;          /* BIGNUM context */
-       DSA_SIG *sdsa;          /* DSA signature context fake */
+       DSA_SIG *sdsa;          /* DSA parameters */
        BIGNUM  *bn, *bk;
        u_int   len;
        u_char  *ptr;
@@ -2215,9 +2281,9 @@ crypto_alice2(
        }
        if (peer->ident_pkey != NULL)
                EVP_PKEY_free(peer->ident_pkey);
-       snprintf(filename, MAXFILENAME, "ntpkey_gqpar_%s",
+       snprintf(filename, MAXFILENAME, "ntpkey_gq_%s",
            peer->issuer);
-       peer->ident_pkey = crypto_key(filename, &fstamp, TYPE_PRIVATE);
+       peer->ident_pkey = crypto_key(filename, &fstamp);
        if (peer->ident_pkey == NULL) {
                msyslog(LOG_ERR,
                    "crypto_alice: file %s not found or corrupt",
@@ -2278,13 +2344,13 @@ crypto_bob2(
        struct value *vp        /* value pointer */
        )
 {
-       RSA     *rsapar;        /* GQ parameters */
-       RSA     *rsa;           /* GQ keys */
-       DSA_SIG *sdsa;          /* RSA signature context fake */
+       RSA     *rsa;           /* GQ parameters */
+       DSA_SIG *sdsa;          /* DSA parameters */
        BN_CTX  *bctx;          /* BIGNUM context */
        EVP_MD_CTX ctx;         /* signature context */
        tstamp_t tstamp;        /* NTP timestamp */
        BIGNUM  *r, *k, *g, *y;
+       u_char  *ptr;
        u_int   len;
 
        /*
@@ -2295,8 +2361,7 @@ crypto_bob2(
                msyslog(LOG_ERR, "crypto_bob2: GQ unavailable");
                return (XEVNT_PUB);
        }
-       rsapar = gqpar_pkey->pkey.rsa;
-       rsa = gq_pkey->pkey.rsa;
+       rsa = gqpar_pkey->pkey.rsa;
 
        /*
         * Extract r from the challenge.
@@ -2315,11 +2380,11 @@ crypto_bob2(
        bctx = BN_CTX_new(); k = BN_new(); g = BN_new(); y = BN_new();
        sdsa = DSA_SIG_new();
        BN_rand(k, len * 8, -1, 1);             /* k */
-       BN_mod(k, k, rsapar->n, bctx);
-       BN_mod_exp(y, rsa->n, r, rsapar->n, bctx); /* u^r mod n */
-       BN_mod_mul(y, k, y, rsapar->n, bctx);   /* k u^r mod n */
+       BN_mod(k, k, rsa->n, bctx);
+       BN_mod_exp(y, rsa->p, r, rsa->n, bctx); /* u^r mod n */
+       BN_mod_mul(y, k, y, rsa->n, bctx);      /* k u^r mod n */
        sdsa->r = BN_dup(y);
-       BN_mod_exp(g, k, rsapar->e, rsapar->n, bctx); /* k^b mod n */
+       BN_mod_exp(g, k, rsa->e, rsa->n, bctx); /* k^b mod n */
        bighash(g, g);
        sdsa->s = BN_dup(g);
        BN_CTX_free(bctx);
@@ -2340,8 +2405,9 @@ crypto_bob2(
                return (XEVNT_PUB);
        }
        vp->vallen = htonl(len);
-       vp->ptr = emalloc(len);
-       i2d_DSA_SIG(sdsa, (u_char **)&vp->ptr);
+       ptr = emalloc(len);
+       vp->ptr = ptr;
+       i2d_DSA_SIG(sdsa, &ptr);
        DSA_SIG_free(sdsa);
        vp->siglen = 0;
        if (tstamp == 0)
@@ -2370,12 +2436,12 @@ crypto_gq(
        struct peer *peer       /* peer structure pointer */
        )
 {
-       RSA     *rsapar;        /* GQ parameters */
+       RSA     *rsa;           /* GQ parameters */
        BN_CTX  *bctx;          /* BIGNUM context */
        DSA_SIG *sdsa;          /* RSA signature context fake */
        BIGNUM  *y, *v;
-       u_int   len;
        u_char  *ptr;
+       u_int   len;
        int     temp;
 
        /*
@@ -2387,7 +2453,7 @@ crypto_gq(
                msyslog(LOG_ERR, "crypto_gq: GQ unavailable");
                return (XEVNT_PUB);
        }
-       if ((rsapar = peer->ident_pkey->pkey.rsa) == NULL) {
+       if ((rsa = peer->ident_pkey->pkey.rsa) == NULL) {
                msyslog(LOG_ERR, "crypto_alice: GQ defective key");
                return (XEVNT_PUB);
        }
@@ -2411,11 +2477,10 @@ crypto_gq(
        /*
         * Alice computes v^r y^b mod n.
         */
-       BN_mod_exp(v, peer->grpkey, peer->iffval, rsapar->n, bctx);
+       BN_mod_exp(v, peer->grpkey, peer->iffval, rsa->n, bctx);
                                                /* v^r mod n */
-       BN_mod_exp(y, sdsa->r, rsapar->e, rsapar->n, bctx);
-                                               /* y^b mod n */
-       BN_mod_mul(y, v, y, rsapar->n, bctx);   /* v^r y^b mod n */
+       BN_mod_exp(y, sdsa->r, rsa->e, rsa->n, bctx); /* y^b mod n */
+       BN_mod_mul(y, v, y, rsa->n, bctx);      /* v^r y^b mod n */
 
        /*
         * The result should match the hash of g^k mod n.
@@ -2433,6 +2498,334 @@ crypto_gq(
 }
 
 
+/*
+ ***********************************************************************
+ *                                                                    *
+ * The following routines implement the Mu-Varadharajan (MV) identity  *
+ * scheme                                                              *
+ *                                                                    *
+ ***********************************************************************
+ */
+/*
+ * The Mu-Varadharajan (MV) cryptosystem was originally intended when
+ * servers broadcast messages to clients, but clients never send
+ * messages to servers. There is one encryption key for the server and a
+ * separate decryption key for each client. It operated something like a
+ * pay-per-view satellite broadcasting system where the session key is
+ * encrypted by the broadcaster and the decryption keys are held in a
+ * tamperproof set-top box.
+ *
+ * The MV parameters and private encryption key hide in a DSA cuckoo
+ * structure which uses the same parameters, but generated in a
+ * different way. The values are used in an encryption scheme similar to
+ * El Gamal cryptography and a polynomial formed from the expansion of
+ * product terms (x - x[j]), as described in Mu, Y., and V.
+ * Varadharajan: Robust and Secure Broadcasting, Proc. Indocrypt 2001,
+ * 223-231. The paper has significant errors and serious omissions.
+ *
+ * Let q be the product of n distinct primes s'[j] (j = 1...n), where
+ * each s'[j] has m significant bits. Let p be a prime p = 2 * q + 1, so
+ * that q and each s'[j] divide p - 1 and p has M = n * m + 1
+ * significant bits. The elements x mod q of Zq with the elements 2 and
+ * the primes removed form a field Zq* valid for polynomial arithetic.
+ * Let g be a generator of Zp; that is, gcd(g, p - 1) = 1 and g^q = 1
+ * mod p. We expect M to be in the 500-bit range and n relatively small,
+ * like 25, so the likelihood of a randomly generated element of x mod q
+ * of Zq colliding with a factor of p - 1 is very small and can be
+ * avoided. Associated with each s'[j] is an element s[j] such that s[j]
+ * s'[j] = s'[j] mod q. We find s[j] as the quotient (q + s'[j]) /
+ * s'[j]. These are the parameters of the scheme and they are expensive
+ * to compute.
+ *
+ * We set up an instance of the scheme as follows. A set of random
+ * values x[j] mod q (j = 1...n), are generated as the zeros of a
+ * polynomial of order n. The product terms (x - x[j]) are expanded to
+ * form coefficients a[i] mod q (i = 0...n) in powers of x. These are
+ * used as exponents of the generator g mod p to generate the private
+ * encryption key A. The pair (gbar, ghat) of public server keys and the
+ * pairs (xbar[j], xhat[j]) (j = 1...n) of private client keys are used
+ * to construct the decryption keys. The devil is in the details.
+ *
+ * The distinguishing characteristic of this scheme is the capability to
+ * revoke keys. Included in the calculation of E, gbar and ghat is the
+ * product s = prod(s'[j]) (j = 1...n) above. If the factor s'[j] is
+ * subsequently removed from the product and E, gbar and ghat
+ * recomputed, the jth client will no longer be able to compute E^-1 and
+ * thus unable to decrypt the block.
+ *
+ * How it works
+ *
+ * The scheme goes like this. Bob has the server values (p, A, q, gbar,
+ * ghat) and Alice the client values (p, xbar, xhat).
+ *
+ * Alice rolls new random challenge r (0 < r < p) and sends to Bob in
+ * the MV request message. Bob rolls new random k (0 < k < q), encrypts
+ * y = A^k mod p (a permutation) and sends (hash(y), gbar^k, ghat^k) to
+ * Alice.
+ * 
+ * Alice receives the response and computes the decryption key (the
+ * inverse permutation) from previously obtained (xbar, xhat) and
+ * (gbar^k, ghat^k) in the message. She computes the inverse, which is
+ * unique by reasons explained in the ntp-keygen.c program sources. If
+ * the hash of this result matches hash(y), Alice knows that Bob has the
+ * group key b. The signed response binds this knowledge to Bob's
+ * private key and the public key previously received in his
+ * certificate.
+ *
+ * crypto_alice3 - construct Alice's challenge in MV scheme
+ *
+ * Returns
+ * XEVNT_OK    success
+ * XEVNT_PUB   bad or missing public key
+ */
+static int
+crypto_alice3(
+       struct peer *peer,      /* peer pointer */
+       struct value *vp        /* value pointer */
+       )
+{
+       DSA     *dsa;           /* MV parameters */
+       BN_CTX  *bctx;          /* BIGNUM context */
+       EVP_MD_CTX ctx;         /* signature context */
+       char    filename[MAXFILENAME + 1];
+       tstamp_t tstamp;
+       tstamp_t fstamp;
+       u_int   len;
+
+       /*
+        * If there is no trusted host, something awful happened.
+        * Otherwise, try to load the identity file containing the
+        * scheme parameters. If the file does not exist, not to worry;
+        * the client does not need identity confirmation. If it does
+        * exist, it must have correct format and content.
+        */
+       if (peer->issuer == NULL) {
+               msyslog(LOG_ERR, "crypto_alice: MV unavailable");
+               return (XEVNT_PUB);
+       }
+       if (peer->ident_pkey != NULL)
+               EVP_PKEY_free(peer->ident_pkey);
+       snprintf(filename, MAXFILENAME, "ntpkey_mvkey_%s",
+           peer->issuer);
+       peer->ident_pkey = crypto_key(filename, &fstamp);
+       if (peer->ident_pkey == NULL) {
+               peer->crypto |= CRYPTO_FLAG_VRFY;
+               return (XEVNT_OK);
+       }
+       if ((dsa = peer->ident_pkey->pkey.dsa) == NULL) {
+               msyslog(LOG_ERR, "crypto_alice: MV defective key");
+               return (XEVNT_PUB);
+       }
+
+       /*
+        * Roll new random r (0 < r < q). The OpenSSL library has a bug
+        * omitting BN_rand_range, so we have to do it the hard way.
+        */
+       bctx = BN_CTX_new();
+       len = BN_num_bytes(dsa->p);
+       if (peer->iffval != NULL)
+               BN_free(peer->iffval);
+       peer->iffval = BN_new();
+       BN_rand(peer->iffval, len * 8, -1, 1);  /* r */
+       BN_mod(peer->iffval, peer->iffval, dsa->p, bctx);
+       BN_CTX_free(bctx);
+
+       /*
+        * Sign and send to Bob.
+        */
+       tstamp = crypto_time();
+       memset(vp, 0, sizeof(struct value));
+       vp->tstamp = htonl(tstamp);
+       vp->fstamp = hostval.tstamp;
+       vp->vallen = htonl(len);
+       vp->ptr = emalloc(len);
+       BN_bn2bin(peer->iffval, vp->ptr);
+       vp->siglen = 0;
+       if (tstamp == 0)
+               return (XEVNT_OK);
+       vp->sig = emalloc(sign_siglen);
+       EVP_SignInit(&ctx, sign_digest);
+       EVP_SignUpdate(&ctx, (u_char *)&vp->tstamp, 12);
+       EVP_SignUpdate(&ctx, vp->ptr, len);
+       if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey))
+               vp->siglen = htonl(len);
+       return (XEVNT_OK);
+}
+
+
+/*
+ * crypto_bob3 - construct Bob's response to Alice's challenge
+ *
+ * Returns
+ * XEVNT_OK    success
+ * XEVNT_PUB   bad or missing public key
+ */
+static int
+crypto_bob3(
+       struct exten *ep,       /* extension pointer */
+       struct value *vp        /* value pointer */
+       )
+{
+       DSA     *dsa;           /* MV parameters */
+       DSA     *sdsa;          /* DSA signature context fake */
+       BN_CTX  *bctx;          /* BIGNUM context */
+       EVP_MD_CTX ctx;         /* signature context */
+       tstamp_t tstamp;        /* NTP timestamp */
+       BIGNUM  *r, *k, *u;
+       u_char  *ptr;
+       u_int   len;
+
+       /*
+        * If the MV parameters are not valid, something awful
+        * happened or we are being tormented.
+        */
+       if (!(crypto_flags & CRYPTO_FLAG_MV)) {
+               msyslog(LOG_ERR, "crypto_bob: MV unavailable");
+               return (XEVNT_PUB);
+       }
+       dsa = mvpar_pkey->pkey.dsa;
+
+       /*
+        * Extract r from the challenge.
+        */
+       len = ntohl(ep->vallen);
+       if ((r = BN_bin2bn((u_char *)ep->pkt, len, NULL)) == NULL) {
+               msyslog(LOG_ERR, "crypto_bob %s\n",
+                   ERR_error_string(ERR_get_error(), NULL));
+               return (XEVNT_PUB);
+       }
+
+       /*
+        * Bob rolls random k (0 < k < q), making sure it is not a
+        * factor of q. He then computes y = A^k r and sends (hash(y),
+        * gbar^k, ghat^k) to Alice.
+        */
+       bctx = BN_CTX_new(); k = BN_new(); u = BN_new();
+       sdsa = DSA_new();
+       sdsa->p = BN_new(); sdsa->q = BN_new(); sdsa->g = BN_new();
+       while (1) {
+               BN_rand(k, BN_num_bits(dsa->q), 0, 0);
+               BN_mod(k, k, dsa->q, bctx);
+               BN_gcd(u, k, dsa->q, bctx);
+               if (BN_is_one(u))
+                       break;
+       }
+       BN_mod_exp(u, dsa->g, k, dsa->p, bctx); /* A r */
+       BN_mod_mul(u, u, r, dsa->p, bctx);
+       bighash(u, sdsa->p);
+       BN_mod_exp(sdsa->q, dsa->priv_key, k, dsa->p, bctx); /* gbar */
+       BN_mod_exp(sdsa->g, dsa->pub_key, k, dsa->p, bctx); /* ghat */
+       BN_CTX_free(bctx); BN_free(k); BN_free(r); BN_free(u);
+
+       /*
+        * Encode the values in ASN.1 and sign.
+        */
+       tstamp = crypto_time();
+       memset(vp, 0, sizeof(struct value));
+       vp->tstamp = htonl(tstamp);
+       vp->fstamp = hostval.tstamp;
+       len = i2d_DSAparams(sdsa, NULL);
+       if (len <= 0) {
+               msyslog(LOG_ERR, "crypto_bob %s\n",
+                   ERR_error_string(ERR_get_error(), NULL));
+               DSA_free(sdsa);
+               return (XEVNT_PUB);
+       }
+       vp->vallen = htonl(len);
+       ptr = emalloc(len);
+       vp->ptr = ptr;
+       i2d_DSAparams(sdsa, &ptr);
+       DSA_free(sdsa);
+       vp->siglen = 0;
+       if (tstamp == 0)
+               return (XEVNT_OK);
+       vp->sig = emalloc(sign_siglen);
+       EVP_SignInit(&ctx, sign_digest);
+       EVP_SignUpdate(&ctx, (u_char *)&vp->tstamp, 12);
+       EVP_SignUpdate(&ctx, vp->ptr, len);
+       if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey))
+               vp->siglen = htonl(len);
+       return (XEVNT_OK);
+}
+
+
+/*
+ * crypto_mv - verify Bob's response to Alice's challenge
+ *
+ * Returns
+ * XEVNT_OK    success
+ * XEVNT_PUB   bad or missint public key
+ * XEVNT_ID    bad or missing identification
+ */
+int
+crypto_mv(
+       struct exten *ep,       /* extension pointer */
+       struct peer *peer       /* peer structure pointer */
+       )
+{
+       DSA     *dsa;           /* MV parameters */
+       DSA     *sdsa;          /* DSA parameters */
+       BN_CTX  *bctx;          /* BIGNUM context */
+       BIGNUM  *k, *u, *v;
+       u_int   len;
+       u_char  *ptr;
+       int     temp;
+
+       /*
+        * If the MV parameters are not valid or no challenge was sent,
+        * something awful happened or we are being tormented.
+        */
+       if (!(peer->crypto & CRYPTO_FLAG_MV) || peer->ident_pkey ==
+           NULL) {
+               msyslog(LOG_ERR, "crypto_mv: MV unavailable");
+               return (XEVNT_PUB);
+       }
+       if ((dsa = peer->ident_pkey->pkey.dsa) == NULL) {
+               msyslog(LOG_ERR, "crypto_alice: MV defective key");
+               return (XEVNT_PUB);
+       }
+       if (peer->iffval == NULL) {
+               msyslog(LOG_ERR, "crypto_mv: missing MV challenge");
+               return (XEVNT_PUB);
+       }
+
+       /*
+        * Extract the (hash(y), gbar, ghat) values from the response.
+        */
+       bctx = BN_CTX_new(); k = BN_new(); u = BN_new(); v = BN_new();
+       len = ntohl(ep->vallen);
+       ptr = (u_char *)ep->pkt;
+       if ((sdsa = d2i_DSAparams(NULL, &ptr, len)) == NULL) {
+               msyslog(LOG_ERR, "crypto_mv %s\n",
+                   ERR_error_string(ERR_get_error(), NULL));
+               return (XEVNT_PUB);
+       }
+
+       /*
+        * Compute (gbar^xhat ghat^xbar)^-1 mod p.
+        */
+       BN_mod_exp(u, sdsa->q, dsa->pub_key, dsa->p, bctx);
+       BN_mod_exp(v, sdsa->g, dsa->priv_key, dsa->p, bctx);
+       BN_mod_mul(u, u, v, dsa->p, bctx);
+       BN_mod_inverse(u, u, dsa->p, bctx);
+       BN_mod_mul(v, u, peer->iffval, dsa->p, bctx);
+
+       /*
+        * The result should match the hash of r mod p.
+        */
+       bighash(v, v);
+       temp = BN_cmp(v, sdsa->p);
+       BN_CTX_free(bctx); BN_free(k); BN_free(u); BN_free(v);
+       BN_free(peer->iffval);
+       peer->iffval = NULL;
+       DSA_free(sdsa);
+       if (temp == 0)
+               return (XEVNT_OK);
+       else
+               return (XEVNT_ID);
+}
+
+
 /*
  ***********************************************************************
  *                                                                    *
@@ -2908,8 +3301,9 @@ cert_install(
                         * this is an identity scheme, fetch the
                         * identity credentials.
                         */
-                       if (peer->crypto & crypto_flags &
-                           (CRYPTO_FLAG_IFF | CRYPTO_FLAG_GQ))
+                       if ((peer->crypto & crypto_flags &
+                           (CRYPTO_FLAG_IFF | CRYPTO_FLAG_GQ)) |
+                           (peer->crypto & CRYPTO_FLAG_MV))
                                continue;
 
                        peer->crypto |= CRYPTO_FLAG_VRFY;
@@ -2964,13 +3358,11 @@ cert_free(
 static EVP_PKEY *
 crypto_key(
        char    *cp,            /* file name */
-       tstamp_t *fstamp,       /* filestamp */
-       int     type            /* file type */
+       tstamp_t *fstamp        /* filestamp */
        )
 {
        FILE    *str;           /* file handle */
        EVP_PKEY *pkey = NULL;  /* public/private key */
-       RSA     *rsa;           /* RSA public key */
        char    filename[MAXFILENAME]; /* name of key file */
        char    linkname[MAXFILENAME]; /* file link (for filestamp) */
        u_char  statstr[NTP_MAXSTRLEN]; /* statistics for filegen */
@@ -2991,23 +3383,9 @@ crypto_key(
                return (NULL);
 
        /*
-        * Read and decrypt PEM-encoded key.
+        * Read and decrypt PEM-encoded private key.
         */
-       switch (type) {
-       case TYPE_PRIVATE:
-               pkey = PEM_read_PrivateKey(str, NULL, NULL, passwd);
-               break;
-
-       case TYPE_PUBLIC:
-               rsa = PEM_read_RSAPublicKey(str, NULL, NULL, NULL);
-               if (rsa != NULL) {
-                       pkey = EVP_PKEY_new();
-                       EVP_PKEY_assign_RSA(pkey, rsa);
-               } else {
-                       pkey = NULL;
-               }
-               break;
-       }
+       pkey = PEM_read_PrivateKey(str, NULL, NULL, passwd);
        fclose(str);
        if (pkey == NULL) {
                msyslog(LOG_ERR, "crypto_key %s\n",
@@ -3279,15 +3657,11 @@ void
 crypto_setup(void)
 {
        EVP_PKEY *pkey;         /* private/public key pair */
-       RSA     *rsapar;        /* RSA parameters cuckoo */
-       RSA     *rsa;           /* RSA keys cuckoo */
        char    filename[MAXFILENAME]; /* name of host file */
        l_fp    seed;           /* crypto PRNG seed as NTP timestamp */
        tstamp_t fstamp;        /* filestamp */
        tstamp_t sstamp;        /* sign filestamp */
-       BN_CTX  *bctx;
-       BIGNUM  *bn;
-       u_int   len, bytes, temp;
+       u_int   len, bytes;
        u_char  *ptr;
 
        /*
@@ -3352,7 +3726,7 @@ crypto_setup(void)
                host_file = emalloc(strlen(filename) + 1);
                strcpy(host_file, filename);
        }
-       pkey = crypto_key(host_file, &fstamp, TYPE_PRIVATE);
+       pkey = crypto_key(host_file, &fstamp);
        if (pkey == NULL) {
                msyslog(LOG_ERR,
                    "host key file %s not found or corrupt",
@@ -3389,7 +3763,7 @@ crypto_setup(void)
                sign_file = emalloc(strlen(filename) + 1);
                strcpy(sign_file, filename);
        }
-       pkey = crypto_key(sign_file, &fstamp, TYPE_PRIVATE);
+       pkey = crypto_key(sign_file, &fstamp);
        if (pkey != NULL) {
                sign_pkey = pkey;
                sstamp = fstamp;
@@ -3397,65 +3771,44 @@ crypto_setup(void)
        sign_siglen = EVP_PKEY_size(sign_pkey);
 
        /*
-        * Load optional IFF key from file "ntpkey_iff_<hostname>".
+        * Load optional IFF parameters from file
+        * "ntpkey_iff_<hostname>".
         */
-       if (iff_file == NULL) {
+       if (iffpar_file == NULL) {
                snprintf(filename, MAXFILENAME, "ntpkey_iff_%s",
                    sys_hostname);
-               iff_file = emalloc(strlen(filename) + 1);
-               strcpy(iff_file, filename);
+               iffpar_file = emalloc(strlen(filename) + 1);
+               strcpy(iffpar_file, filename);
        }
-       pkey = crypto_key(iff_file, &fstamp, TYPE_PRIVATE);
-       if (pkey != NULL) {
-               iff_pkey = pkey;
+       iffpar_pkey = crypto_key(iffpar_file, &fstamp);
+       if (iffpar_pkey != NULL)
                crypto_flags |= CRYPTO_FLAG_IFF;
-       }
 
        /*
-        * Load optional GQ parameters from file
-        * "ntpkey_gqpar_<hostname>".
+        * Load optional GQ parameters from file "ntpkey_gq_<hostname>".
         */
        if (gqpar_file == NULL) {
-               snprintf(filename, MAXFILENAME, "ntpkey_gqpar_%s",
+               snprintf(filename, MAXFILENAME, "ntpkey_gq_%s",
                    sys_hostname);
                gqpar_file = emalloc(strlen(filename) + 1);
                strcpy(gqpar_file, filename);
        }
-       gqpar_pkey = crypto_key(gqpar_file, &fstamp, TYPE_PRIVATE);
+       gqpar_pkey = crypto_key(gqpar_file, &fstamp);
+       if (gqpar_pkey != NULL)
+               crypto_flags |= CRYPTO_FLAG_GQ;
 
        /*
-        * Load optional GQ key from file "ntpkey_gq_<hostname>".
+        * Load optional MV parameters from file "ntpkey_mv_<hostname>".
         */
-       if (gq_file == NULL) {
-               snprintf(filename, MAXFILENAME, "ntpkey_gq_%s",
+       if (mvpar_file == NULL) {
+               snprintf(filename, MAXFILENAME, "ntpkey_mv_%s",
                    sys_hostname);
-               gq_file = emalloc(strlen(filename) + 1);
-               strcpy(gq_file, filename);
-       }
-       gq_pkey = crypto_key(gq_file, &fstamp, TYPE_PUBLIC);
-
-       /*
-        * If both GQ parameter and keys are present, verify the product
-        * u^b (u^-1)^b = 1 mod n.
-        */
-       if (gqpar_pkey != NULL && gq_pkey != NULL) {
-               bctx = BN_CTX_new();
-               bn = BN_new();
-               rsapar = gqpar_pkey->pkey.rsa;
-               rsa = gq_pkey->pkey.rsa;
-               BN_mod_exp(bn, rsa->n, rsapar->e, rsapar->n, bctx);
-               BN_mod_mul(bn, bn, rsa->e, rsapar->n, bctx);
-               temp = BN_is_one(bn);
-               BN_free(bn);
-               BN_CTX_free(bctx);
-               if (temp) {
-                       crypto_flags |= CRYPTO_FLAG_GQ;
-               } else {
-                       msyslog(LOG_ERR,
-                           "Mismatched GQ parameters and keys");
-                       exit (-1);
-               }
+               mvpar_file = emalloc(strlen(filename) + 1);
+               strcpy(mvpar_file, filename);
        }
+       mvpar_pkey = crypto_key(mvpar_file, &fstamp);
+       if (mvpar_pkey != NULL)
+               crypto_flags |= CRYPTO_FLAG_MV;
 
        /*
         * Load required certificate from file "ntpkey_cert_<hostname>".
@@ -3567,19 +3920,27 @@ crypto_config(
                break;
 
        /*
-        * Set iff key file name.
+        * Set iff parameters file name.
+        */
+       case CRYPTO_CONF_IFFPAR:
+               iffpar_file = emalloc(strlen(cp) + 1);
+               strcpy(iffpar_file, cp);
+               break;
+
+       /*
+        * Set gq parameters file name.
         */
-       case CRYPTO_CONF_IFF:
-               iff_file = emalloc(strlen(cp) + 1);
-               strcpy(iff_file, cp);
+       case CRYPTO_CONF_GQPAR:
+               gqpar_file = emalloc(strlen(cp) + 1);
+               strcpy(gqpar_file, cp);
                break;
 
        /*
-        * Set gq key file name.
+        * Set mv parameters file name.
         */
-       case CRYPTO_CONF_GQ:
-               gq_file = emalloc(strlen(cp) + 1);
-               strcpy(gq_file, cp);
+       case CRYPTO_CONF_MVPAR:
+               mvpar_file = emalloc(strlen(cp) + 1);
+               strcpy(mvpar_file, cp);
                break;
 
        /*
index cf8a33f689704b71f8927790d8d6f038c8901bde..6705bb0fa5e706ffd5c5a9035ff94b0288b52812 100644 (file)
@@ -2407,13 +2407,16 @@ peer_xmit(
                                    sys_hostname);
                        else if (!(peer->crypto & CRYPTO_FLAG_VALID))
                                exten = crypto_args(peer, CRYPTO_CERT,
-                                   peer->subject);
+                                   peer->issuer);
 
                        /*
-                        * Identity. Note we have to sign the
-                        * certificate before the cookie to avoid a
-                        * deadlock when the passive peer is walking the
-                        * certificate trail. Awesome.
+                        * Identity.  We look first for GQ, then IFF. If
+                        * the server has MV, then we look for that. If
+                        * not found, we skip identity confirmation.
+                        * Note we have to sign the certificate before
+                        * the cookie to avoid a deadlock when the
+                        * passive peer is walking the certificate
+                        * trail. Awesome.
                         */
                        else if (!(peer->crypto & CRYPTO_FLAG_VRFY) &&
                            crypto_flags & peer->crypto &
@@ -2425,9 +2428,10 @@ peer_xmit(
                            CRYPTO_FLAG_IFF)
                                exten = crypto_args(peer, CRYPTO_IFF,
                                    NULL);
-                       else if (!(peer->crypto & CRYPTO_FLAG_VRFY))
-                               exten = crypto_args(peer, CRYPTO_CERT,
-                                   peer->issuer);
+                       else if (!(peer->crypto & CRYPTO_FLAG_VRFY) &&
+                           peer->crypto & CRYPTO_FLAG_MV)
+                               exten = crypto_args(peer, CRYPTO_MV,
+                                   NULL);
                        else if (sys_leap != LEAP_NOTINSYNC &&
                           !(peer->crypto & CRYPTO_FLAG_SIGN))
                                exten = crypto_args(peer, CRYPTO_SIGN,
@@ -2509,7 +2513,9 @@ peer_xmit(
                                    peer->issuer);
 
                        /*
-                        * Identity
+                        * Identity. We look first for GQ, then IFF. If
+                        * the server has MV, then we look for that. If
+                        * not found, we skip identity confirmation.
                         */
                        else if (!(peer->crypto & CRYPTO_FLAG_VRFY) &&
                            crypto_flags & peer->crypto &
@@ -2521,6 +2527,10 @@ peer_xmit(
                            CRYPTO_FLAG_IFF)
                                exten = crypto_args(peer, CRYPTO_IFF,
                                    NULL);
+                       else if (!(peer->crypto & CRYPTO_FLAG_VRFY) &&
+                           peer->crypto & CRYPTO_FLAG_MV)
+                               exten = crypto_args(peer, CRYPTO_MV,
+                                   NULL);
 
                        /*
                         * Autokey
index e4a4109ec2cb1ba867af0d778a03a111f69f4f6d..9f169962f63b69ef3978de13f679467374a0f0b8 100644 (file)
@@ -10,7 +10,7 @@
  * Files are prefixed with a header giving the name and date of creation
  * followed by a type-specific descriptive label and PEM-encoded data
  * string compatible with programs of the OpenSSL library.
- *
+ *_assign
  * Note that private keys can be password encrypted as per OpenSSL
  * conventions.
  *
  *     Schnorr (IFF) parameters used to verify trusted group membership
  *
  * ntpkey_GQpar_<hostname>.<filestamp>,
- * ntpkey_GQkey_<hostname>.<filestamp>
- *     Guillou-Quisquater (GQ) parameters and keys used to verify
- *     trusted group membership
+ *     Guillou-Quisquater (GQ) parameters used to verify trusted group
+ *     membership
  *
- * ntpkey_MVkey_<hostname>.<filestamp>,
- * ntpkey_MPkey<n>_<hostname>.<filestamp>
- *     Mu-Varadharajan (MV) parameters and keys used to verify trusted
- *      group membership
+ * ntpkey_MVpar_<hostname>.<filestamp>,
+ *     Mu-Varadharajan (MV) parameters used to verify  trusted group
+ *     membership
  *
  * ntpkey_XXXcert_<hostname>.<filestamp>
  *     X509v3 certificate using RSA or DSA public keys and signatures.
  * The links produced include
  *
  * ntpkey_key_<hostname> (RSA)
- *     Public host key used for cookie encryption and digital
- *     signatures if a sign key is not present. The public key value of
- *     the key is disclosed in certificates only if signed by this key.
+ *     Host public/private key pair used for cookie encryption and
+ *     digital signatures if a sign key is not present. 
  *
  * ntpkey_sign_<hostname> (RSA or DSA)
- *     Public sign key used for digital signatures. The public key
- *     value of the key is disclosed in certificates signed by this
- *     key unless declared otherwise.
+ *     Sign public/private key pair used for digital signatures.
+ *
+ * ntpkey_iffpar_<hostname> (IFF)
+ *     Private IFF parameters used to securely confirm identity to
+ *     other members of the group.
  *
- * ntpkey_iff_<hostname> (IFF)
- *     Private IFF key used to securely confirm identity to other
- *     members of the group. No value of this key is disclosed except
- *     to other members of the same group.
+ * ntpkey_gqpar_<hostname> (GQ)
+ *     Private GQ parameters used to securely confirm identity to other
+ *     members of the group. The public key value is disclosed in
+ *     certificates.
  *
- * ntpkey_gq_<hostname> (GQ)
- *     Public GQ key used to securely confirm identity. The public key
- *     value of the key is disclosed in certificates signed by this
- *     key unless declared otherwise.
+ * ntpkey_mvpar_<hostname> (MV)
+ *     Private MV parameters used to securely confirm identity. to
+ *     other members of the group.
  *
  * Note: Once in a while because of some statistical fluke this program
  * fails to generate and verify some cryptographic data, as indicated by
@@ -149,10 +147,10 @@ int       gen_md5         P((char *));
 #ifdef OPENSSL
 EVP_PKEY *gen_rsa      P((char *));
 EVP_PKEY *gen_dsa      P((char *));
-EVP_PKEY *gen_iff      P((char *));
-RSA    *gen_gqpar      P((char *));
-RSA    *gen_gqkey      P((char *, RSA *));
-void   gen_mv          P((char *));
+EVP_PKEY *gen_iffpar   P((char *));
+EVP_PKEY *gen_gqpar    P((char *));
+EVP_PKEY *gen_gqkey    P((char *, EVP_PKEY *));
+EVP_PKEY *gen_mvpar    P((char *));
 int    x509            P((EVP_PKEY *, const EVP_MD *, char *, char *));
 void   cb              P((int, int, void *));
 EVP_PKEY *genkey       P((char *, char *));
@@ -190,20 +188,22 @@ main(
        X509    *cert = NULL;   /* X509 certificate */
        EVP_PKEY *pkey_host = NULL; /* host key */
        EVP_PKEY *pkey_sign = NULL; /* sign key */
-       EVP_PKEY *pkey_iff = NULL; /* iff group key */
-       EVP_PKEY *pkey = NULL;  /* temp sign key */
-       RSA     *rsa_gqpar = NULL; /* GQ parameters */
-       RSA     *rsa_gqkey = NULL; /* GQ key */
+       EVP_PKEY *pkey_iff = NULL; /* IFF parameters */
+       EVP_PKEY *pkey_gq = NULL; /* GQ parameters */
+       EVP_PKEY *pkey_mv = NULL; /* MV parameters */
+       int     md5key = 0;     /* generate MD5 keys */
+       int     hostkey = 0;    /* generate RSA keys */
+       int     iffkey = 0;     /* generate IFF parameters */
+       int     gqpar = 0;      /* generate GQ parameters */
+       int     gqkey = 0;      /* update GQ keys */
+       int     mvpar = 0;      /* generate MV parameters */
+       int     mvkey = 0;      /* update MV keys */
+       char    *sign = NULL;   /* sign key */
+       EVP_PKEY *pkey = NULL;  /* temp key */
        const EVP_MD *ectx;     /* EVP digest */
        char    hostbuf[MAXHOSTNAME + 1];
        char    pathbuf[MAXFILENAME + 1];
        FILE    *str;           /* file handle */
-       int     md5key = 0;     /* MD5 keys */
-       int     hostkey = 0;    /* RSA keys */
-       int     iffkey = 0;     /* IFF keys */
-       int     gqpar = 0;      /* GQ parameters */
-       int     gqkey = 0;      /* GQ keys */
-       char    *sign = NULL;   /* sign key */
        const char *scheme = NULL; /* digest/signature scheme */
        char    *exten = NULL;  /* private extension */
        char    *grpkey = NULL; /* identity extension */
@@ -217,7 +217,7 @@ main(
                        OPENSSL_VERSION_NUMBER, SSLeay());
                return (-1);
        } else {
-               printf("OpenSSL version %lx\n", SSLeay());
+               printf("Using OpenSSL version %lx\n", SSLeay());
        }
 #endif /* OPENSSL */
 
@@ -232,7 +232,7 @@ main(
        epoch = tv.tv_sec;
        rval = 0;
        while ((temp = getopt(argc, argv,
-           "c:de:GgHIi:Mm:Pp:S:s:TV:")) != -1) {
+           "c:de:GgHIi:Mm:Pp:S:s:TV:v:")) != -1) {
                switch(temp) {
 
                /*
@@ -257,14 +257,14 @@ main(
                        continue;
 
                /*
-                * -G generate GQ parameters (GQ scheme)
+                * -G generate GQ parameters and keys
                 */
                case 'G':
                        gqpar++;
                        continue;
 
                /*
-                * -g generate GQ keys (GQ scheme)
+                * -g update GQ keys
                 */
                case 'g':
                        gqkey++;
@@ -278,7 +278,7 @@ main(
                        continue;
 
                /*
-                * -I generate IFF parameters (IFF scheme)
+                * -I generate IFF parameters
                 */
                case 'I':
                        iffkey++;
@@ -289,6 +289,7 @@ main(
                 */
                case 'i':
                        trustname = optarg;
+                       continue;
 
                /*
                 * -M generate MD5 keys
@@ -308,7 +309,7 @@ main(
                        continue;
                
                /*
-                * -P generate private certificate (PC scheme)
+                * -P generate PC private certificate
                 */
                case 'P':
                        exten = EXT_KEY_PRIVATE;
@@ -333,6 +334,7 @@ main(
                 */
                case 's':
                        hostname = optarg;
+                       continue;
                
                /*
                 * -T trusted certificate (TC scheme)
@@ -342,19 +344,30 @@ main(
                        continue;
 
                /*
-                * -V <keys> Mu-Varadharajan (MV scheme)
+                * -V <keys> generate MV parameters
                 */
                case 'V':
+                       mvpar++;
                        if (sscanf(optarg, "%d", &nkeys) != 1)
                                printf("invalid option -V %s\n",
                                    optarg);
                        continue;
 
+               /*
+                * -v <key> update MV keys
+                */
+               case 'v':
+                       mvkey++;
+                       if (sscanf(optarg, "%d", &nkeys) != 1)
+                               printf("invalid option -v %s\n",
+                                   optarg);
+                       continue;
+
                /*
                 * None of the above.
                 */
                default:
-                       printf("unknown option %c\n", temp);
+                       printf("Option ignored\n");
                        continue;
                }
        }
@@ -390,11 +403,11 @@ main(
        if (sign != NULL)
                pkey_sign = genkey(sign, "sign");
        if (iffkey)
-               pkey_iff = gen_iff("iff");
+               pkey_iff = gen_iffpar("iff");
        if (gqpar)
-               rsa_gqpar = gen_gqpar("gqpar");
-       if (nkeys > 0)
-               gen_mv("mvkey");
+               pkey_gq = gen_gqpar("gq");
+       if (mvpar)
+               pkey_mv = gen_mvpar("mv");
 
        /*
         * If there is no new host key, look for an existing one. If not
@@ -405,16 +418,14 @@ main(
                if ((str = fopen(filename, "r")) != NULL) {
                        pkey_host = PEM_read_PrivateKey(str, NULL, NULL,
                            passwd);
-
                        fclose(str);
-                       readlink(filename, filename, sizeof(filename));
+                       readlink(filename, filename,  sizeof(filename));
                        if (pkey_host == NULL) {
                                printf("Host key\n%s\n",
                                    ERR_error_string(ERR_get_error(),
                                    NULL));
                        } else {
-                               printf("Using host key %s\n",
-                                   filename);
+                               printf("Using host key %s\n", filename);
                                break;
                        }
                }
@@ -439,9 +450,7 @@ main(
                                    ERR_error_string(ERR_get_error(),
                                    NULL));
                        } else {
-                               pkey = pkey_sign;
-                               printf("Using sign key %s\n",
-                                   filename);
+                               printf("Using sign key %s\n", filename);
                                break;
                        }
                }
@@ -451,53 +460,54 @@ main(
        }
 
        /*
-        * If there is no IFF group key, look for an existing one.
+        * If there is no new IFF file, look for an existing one.
         */
        if (pkey_iff == NULL) {
-               sprintf(filename, "ntpkey_iffpar_%s", hostname);
+               sprintf(filename, "ntpkey_iff_%s", hostname);
                if ((str = fopen(filename, "r")) != NULL) {
-                       pkey_iff = PEM_read_PrivateKey(str, NULL, NULL,
-                           passwd);
+                       pkey_iff = PEM_read_PrivateKey(str, NULL,
+                           NULL, passwd);
                        fclose(str);
                        readlink(filename, filename, sizeof(filename));
                        if (pkey_iff == NULL) {
-                               printf("IFFpar\n%s\n",
+                               printf("IFF parameters\n%s\n",
                                    ERR_error_string(ERR_get_error(),
                                    NULL));
                        } else {
-                               printf("Using IFF group key %s\n",
+                               printf("Using IFF parameters %s\n",
                                    filename);
                        }
                }
        }
 
        /*
-        * If there is no GQ group key, look for an existing one.
+        * If there is no new GQ file, look for an existing one.
         */
-       if (rsa_gqpar == NULL) {
-               sprintf(filename, "ntpkey_gqpar_%s", hostname);
+       if (pkey_gq == NULL) {
+               sprintf(filename, "ntpkey_gq_%s", hostname);
                if ((str = fopen(filename, "r")) != NULL) {
-                       rsa_gqpar = PEM_read_RSAPrivateKey(str, NULL,
-                           NULL, passwd);
+                       pkey_gq = PEM_read_PrivateKey(str, NULL, NULL,
+                           passwd);
                        fclose(str);
                        readlink(filename, filename, sizeof(filename));
-                       if (rsa_gqpar == NULL) {
-                               printf("GQpar\n%s\n",
+                       if (pkey_gq == NULL) {
+                               printf("GQ parameters\n%s\n",
                                    ERR_error_string(ERR_get_error(),
                                    NULL));
                        } else {
-                               printf("Using GQ group key %s\n",
+                               printf("Using GQ parameters %s\n",
                                    filename);
                        }
                }
        }
 
        /*
-        * If there is a GQ group key, create GQ private/public keys.
+        * If there is a GQ parameter file, create GQ private/public
+        * keys and extract the public key for the certificate.
         */
-       if (rsa_gqpar != NULL) {
-               rsa_gqkey = gen_gqkey("gq", rsa_gqpar);
-               grpkey = BN_bn2hex(rsa_gqkey->e);
+       if (pkey_gq != NULL) {
+               gen_gqkey("gq", pkey_gq);
+               grpkey = BN_bn2hex(pkey_gq->pkey.rsa->q);
                }
 
        /*
@@ -547,11 +557,10 @@ main(
                EVP_PKEY_free(pkey_sign);
        if (pkey_iff != NULL)
                EVP_PKEY_free(pkey_iff);
-       if (rsa_gqpar != NULL)
-               RSA_free(rsa_gqpar);
-       if (rsa_gqkey != NULL)
-               RSA_free(rsa_gqkey);
-
+       if (pkey_gq != NULL)
+               EVP_PKEY_free(pkey_gq);
+       if (pkey_mv != NULL)
+               EVP_PKEY_free(pkey_mv);
 #endif /* OPENSSL */
        return (rval);
 }
@@ -625,14 +634,14 @@ gen_md5(
 
 #ifdef OPENSSL
 /*
- * Generate RSA public/private keys
+ * Generate RSA public/private key pair
  */
 EVP_PKEY *                     /* public/private key pair */
 gen_rsa(
        char    *id             /* file name id */
        )
 {
-       EVP_PKEY *pkey;         /* public/private key pair */
+       EVP_PKEY *pkey;         /* private key */
        RSA     *rsa;           /* RSA parameters and key pair */
        FILE    *str;
 
@@ -642,6 +651,7 @@ gen_rsa(
        if (rsa == NULL) {
                printf("RSA generate keys fails\n%s\n",
                    ERR_error_string(ERR_get_error(), NULL));
+               rval = -1;
                return (NULL);
        }
 
@@ -664,27 +674,27 @@ gen_rsa(
         * encoded in PEM.
         */
        str = fheader("RSAkey", hostname);
-       PEM_write_RSAPrivateKey(str, rsa, passwd ? EVP_des_cbc() : NULL,
+       pkey = EVP_PKEY_new();
+       EVP_PKEY_assign_RSA(pkey, rsa);
+       PEM_write_PrivateKey(str, pkey, passwd ? EVP_des_cbc() : NULL,
            NULL, 0, NULL, passwd);
        fclose(str);
        if (debug)
                RSA_print_fp(stdout, rsa, 0);
        fslink(id, hostname);
-       pkey = EVP_PKEY_new();
-       EVP_PKEY_assign_RSA(pkey, rsa);
        return (pkey);
 }
 
  
 /*
- * Generate DSA public/private keys
+ * Generate DSA public/private key pair
  */
 EVP_PKEY *                     /* public/private key pair */
 gen_dsa(
        char    *id             /* file name id */
        )
 {
-       EVP_PKEY *pkey;         /* public/private key pair */
+       EVP_PKEY *pkey;         /* private key */
        DSA     *dsa;           /* DSA parameters */
        u_char  seed[20];       /* seed for parameters */
        FILE    *str;
@@ -721,24 +731,24 @@ gen_dsa(
         * encoded in PEM.
         */
        str = fheader("DSAkey", hostname);
-       PEM_write_DSAPrivateKey(str, dsa, passwd ? EVP_des_cbc() : NULL,
+       pkey = EVP_PKEY_new();
+       EVP_PKEY_assign_DSA(pkey, dsa);
+       PEM_write_PrivateKey(str, pkey, passwd ? EVP_des_cbc() : NULL,
            NULL, 0, NULL, passwd);
        fclose(str);
        if (debug)
                DSA_print_fp(stdout, dsa, 0);
        fslink(id, hostname);
-       pkey = EVP_PKEY_new();
-       EVP_PKEY_assign_DSA(pkey, dsa);
        return (pkey);
 }
 
 
 /*
- * Generate Schnorr (IFF) parameters and public/private keys
+ * Generate Schnorr (IFF) parameters and keys
  *
  * The Schnorr (IFF)identity scheme is intended for use when
  * certificates are generated by some other trusted certificate
- * authority and the group key cannot be conveyed in the certificate
+ * authority and the parameters cannot be conveyed in the certificate
  * itself. For this purpose, new generations of IFF values must be
  * securely transmitted to all members of the group before use.
  *
@@ -752,12 +762,12 @@ gen_dsa(
  * certificate or message data. Alice challenges Bob to confirm identity
  * using the protocol described below.
  */
-EVP_PKEY *                     /* IFF parameters and keys */
-gen_iff(
+EVP_PKEY *                     /* DSA cuckoo nest */
+gen_iffpar(
        char    *id             /* file name id */
        )
 {
-       EVP_PKEY *pkey;         /* for PEM structure */
+       EVP_PKEY *pkey;         /* private key */
        DSA     *dsa;           /* DSA parameters */
        u_char  seed[20];       /* seed for parameters */
        BN_CTX  *ctx;           /* BN working space */
@@ -795,7 +805,6 @@ gen_iff(
        BN_mod_exp(bk, dsa->g, a, dsa->p, ctx); /* g^a mod p */
        BN_mod_mul(bk, bk, bn, dsa->p, ctx);
        temp = BN_is_one(bk);
-
        printf("Confirm g^(q - a) g^a = 1 mod p: %s\n", temp == 1 ?
            "yes" : "no");
        if (!temp) {
@@ -804,19 +813,21 @@ gen_iff(
                rval = -1;
                return (NULL);
        }
-       dsa->priv_key = BN_dup(a);
-       dsa->pub_key = BN_dup(bn);
+       dsa->priv_key = BN_dup(a);              /* private key */
+       dsa->pub_key = BN_dup(bn);              /* public key */
 
        /*
         * Here is a trial round of the protocol. First, Alice rolls
-        * random r ( 0 < r < q) and sends it to Bob.
+        * random r ( 0 < r < q) and sends it to Bob. She needs only
+        * modulus q.
         */
        BN_rand(r, BN_num_bits(dsa->q), -1, 0); /* r, 0 < r < q */
        BN_mod(r, r, dsa->q, ctx);
 
        /*
         * Bob rolls random k (0 < k < q), computes k + a r mod q and
-        * g^k, then sends (k, g) to Alice.
+        * g^k, then sends (k, g) to Alice. He needs only modulus q and
+        * the private key.
         */
        BN_rand(k, BN_num_bits(dsa->q), -1, 0); /* k, 0 < k < q  */
        BN_mod(k, k, dsa->q, ctx);
@@ -826,7 +837,8 @@ gen_iff(
 
        /*
         * Alice computes g^(k + a r) g^(q - a) r and verifies the
-        * result is equal to g.
+        * result is equal to g. She needs modulus p, generator g, and
+        * the public key, as well as her original r.
         */
        BN_mod_exp(bn, dsa->g, bn, dsa->p, ctx); /* g^(k + a r) mod p */
        BN_mod_exp(bk, dsa->pub_key, r, dsa->p, ctx); /* g^(q - a) r */
@@ -844,13 +856,19 @@ gen_iff(
        }
 
        /*
-        * Write the IFF parameters as a DSA private key encoded in PEM.
-        * Note, this is not encrypted.
+        * Write the IFF parameters and keys as a DSA private key
+        * encoded in PEM.
+        *
+        * p    modulus p
+        * q    modulus q
+        * g    generator g
+        * priv_key a
+        * public_key g^(q - a) mod p
         */
+       str = fheader("IFFpar", trustname);
        pkey = EVP_PKEY_new();
        EVP_PKEY_assign_DSA(pkey, dsa);
-       str = fheader("IFFpar", trustname);
-       PEM_write_DSAPrivateKey(str, dsa, passwd ? EVP_des_cbc() : NULL,
+       PEM_write_PrivateKey(str, pkey, passwd ? EVP_des_cbc() : NULL,
            NULL, 0, NULL, passwd);
        fclose(str);
        if (debug)
@@ -885,15 +903,13 @@ gen_iff(
  * scheme. Alice challenges Bob to confirm identity using the protocol
  * described below.
  */
-/*
- * Generate Guillou-Quisquater (GQ) parameters
- */
-RSA *                          /* RSA cuckoo nest */
+EVP_PKEY *                     /* RSA cuckoo nest */
 gen_gqpar(
        char    *id             /* file name id */
        )
 {
-       RSA     *rsapar;        /* GQ parameters */
+       EVP_PKEY *pkey;         /* private key */
+       RSA     *rsa;           /* GQ parameters */
        BN_CTX  *ctx;           /* BN working space */
        FILE    *str;
 
@@ -901,9 +917,9 @@ gen_gqpar(
         * Generate RSA parameters for use as GQ parameters.
         */
        printf("Generating GQ parameters (%d bits)...\n", modulus);
-       rsapar = RSA_generate_key(modulus, 3, cb, "GQ");
+       rsa = RSA_generate_key(modulus, 3, cb, "GQ");
        printf("\n");
-       if (rsapar == NULL) {
+       if (rsa == NULL) {
                printf("RSA generate keys fails\n%s\n",
                    ERR_error_string(ERR_get_error(), NULL));
                rval = -1;
@@ -913,68 +929,84 @@ gen_gqpar(
        /*
         * Generate the group key b, which is saved in the e member of
         * the RSA structure. These values are distributed to all
-        * members of the group, but shielded from all other groups.
+        * members of the group, but shielded from all other groups. We
+        * don't use all the parameters, but set the unused ones to a
+        * small number to minimize the file size.
         */
        ctx = BN_CTX_new();
-       BN_rand(rsapar->e, BN_num_bits(rsapar->n), -1, 0); /* b */
-       BN_mod(rsapar->e, rsapar->e, rsapar->n, ctx);
+       BN_rand(rsa->e, BN_num_bits(rsa->n), -1, 0); /* b */
+       BN_mod(rsa->e, rsa->e, rsa->n, ctx);
+       BN_copy(rsa->d, BN_value_one());
+       BN_copy(rsa->p, BN_value_one());
+       BN_copy(rsa->q, BN_value_one());
+       BN_copy(rsa->dmp1, BN_value_one());
+       BN_copy(rsa->dmq1, BN_value_one());
+       BN_copy(rsa->iqmp, BN_value_one());
 
        /*
-        * Write the GQ parameters and group key as a RSA private key
-        * encoded in PEM.
+        * Write the GQ parameters as a RSA private key encoded in PEM.
+        * The public and private keys are filled in later.
+        *
+        * n    modulus n
+        * e    group key b
+        * (remaining values are not used)
         */
        str = fheader("GQpar", trustname);
-       PEM_write_RSAPrivateKey(str, rsapar, passwd ? EVP_des_cbc() :
-           NULL, NULL, 0, NULL, passwd);
+       pkey = EVP_PKEY_new();
+       EVP_PKEY_assign_RSA(pkey, rsa);
+       PEM_write_PrivateKey(str, pkey, passwd ? EVP_des_cbc() : NULL,
+           NULL, 0, NULL, passwd);
        fclose(str);
        if (debug)
-               RSA_print_fp(stdout, rsapar, 0);
+               RSA_print_fp(stdout, rsa, 0);
        fslink(id, trustname);
-       return (rsapar);
+       return (pkey);
 }
 
 
 /*
- * Generate Guillou-Quisquater (GQ) public/private keys
+ * Update Guillou-Quisquater (GQ) parameters
  */
-RSA *                          /* GQ public/private key pair */
+EVP_PKEY *                     /* RSA cuckoo nest */
 gen_gqkey(
        char    *id,            /* file name id */
-       RSA     *rsapar         /* GQ parameters */
+       EVP_PKEY *gqpar         /* GQ parameters */
        )
 {
-       RSA     *rsa;           /* GQ public/private key pair */
+       EVP_PKEY *pkey;         /* private key */
+       RSA     *rsa;           /* RSA parameters */
        BN_CTX  *ctx;           /* BN working space */
        BIGNUM  *u, *v, *g, *k, *r, *y; /* BN temps */
        FILE    *str;
        u_int   temp;
 
        /*
-        * Generate GQ key. Note that the group key b is the e member of
+        * Generate GQ keys. Note that the group key b is the e member
+        * of
         * the GQ parameters.
         */
-       printf("Generating GQ keys (%d bits)...\n", modulus);
+       printf("Updating GQ keys (%d bits)...\n", modulus);
        ctx = BN_CTX_new(); u = BN_new(); v = BN_new();
        g = BN_new(); k = BN_new(); r = BN_new(); y = BN_new();
-       rsa = RSA_new();
 
        /*
         * When generating his certificate, Bob rolls random private key
-        * u and inverse u^-1.
+        * u
         */
-       BN_rand(u, BN_num_bits(rsapar->n), -1, 0); /* u */
-       BN_mod(u, u, rsapar->n, ctx);
-       BN_mod_inverse(v, u, rsapar->n, ctx);   /* u^-1 mod n */
-       BN_mod_mul(k, v, u, rsapar->n, ctx);
+       rsa = gqpar->pkey.rsa;
+       BN_rand(u, BN_num_bits(rsa->n), -1, 0); /* u */
+       BN_mod(u, u, rsa->n, ctx);
+       BN_mod_inverse(v, u, rsa->n, ctx);      /* u^-1 mod n */
+       BN_mod_mul(k, v, u, rsa->n, ctx);
 
        /*
-        * Bob computes the public key v = (u^-1)^b, which is saved in
-        * an extension field on his certificate. We check that u^b v =
+        * Bob computes public key v = (u^-1)^b, which is saved in an
+        * extension field on his certificate. We check that u^b v =
         * 1 mod n.
         */
-       BN_mod_exp(v, v, rsapar->e, rsapar->n, ctx);
-       BN_mod_exp(g, u, rsapar->e, rsapar->n, ctx); /* u^b */
-       BN_mod_mul(g, g, v, rsapar->n, ctx); /* u^b (u^-1)^b */
+       BN_mod_exp(v, v, rsa->e, rsa->n, ctx);
+       BN_mod_exp(g, u, rsa->e, rsa->n, ctx); /* u^b */
+       BN_mod_mul(g, g, v, rsa->n, ctx); /* u^b (u^-1)^b */
        temp = BN_is_one(g);
        printf("Confirm u^b (u^-1)^b = 1 mod n: %s\n", temp ? "yes" :
            "no");
@@ -986,34 +1018,37 @@ gen_gqkey(
                rval = -1;
                return (NULL);
        }
-       rsa->n = BN_dup(u);                     /* private key */
-       rsa->e = BN_dup(v);                     /* public key */
+       BN_copy(rsa->p, u);                     /* private key */
+       BN_copy(rsa->q, v);                     /* public key */
 
        /*
-        * Here is a trial run of the protocol. First, Alice fetches
-        * Bob's certificate, then rolls random r (0 < r < n) and sends
-        * it to him.
+        * Here is a trial run of the protocol. First, Alice rolls
+        * random r (0 < r < n) and sends it to Bob. She needs only
+        * modulus n from the parameters.
         */
-       BN_rand(r, BN_num_bits(rsapar->n), -1, 0);      /* r */
-       BN_mod(r, r, rsapar->n, ctx);
+       BN_rand(r, BN_num_bits(rsa->n), -1, 0); /* r */
+       BN_mod(r, r, rsa->n, ctx);
 
        /*
         * Bob rolls random k (0 < k < n), computes y = k u^r mod n and
-        * g = k^b mod n, then sends (y, g) to Alice. 
+        * g = k^b mod n, then sends (y, g) to Alice. He needs modulus n
+        * from the parameters and his private key u. 
         */
-       BN_rand(k, BN_num_bits(rsapar->n), -1, 0);      /* k */
-       BN_mod(k, k, rsapar->n, ctx);
-       BN_mod_exp(y, u, r, rsapar->n, ctx);    /* u^r mod n */
-       BN_mod_mul(y, k, y, rsapar->n, ctx);    /* y = k u^r mod n */
-       BN_mod_exp(g, k, rsapar->e, rsapar->n, ctx); /* g = k^b mod n */
+       BN_rand(k, BN_num_bits(rsa->n), -1, 0); /* k */
+       BN_mod(k, k, rsa->n, ctx);
+       BN_mod_exp(y, rsa->p, r, rsa->n, ctx);  /* u^r mod n */
+       BN_mod_mul(y, k, y, rsa->n, ctx);       /* y = k u^r mod n */
+       BN_mod_exp(g, k, rsa->e, rsa->n, ctx); /* g = k^b mod n */
 
        /*
         * Alice computes v^r y^b mod n and verifies the result is equal
-        * to g.
+        * to g. She needs modulus n, generator g and group key b from
+        * the parameters and Bob's public key v = (u^-1)^b from his
+        * certificate.
         */
-       BN_mod_exp(v, v, r, rsapar->n, ctx);    /* v^r mod n */
-       BN_mod_exp(y, y, rsapar->e, rsapar->n, ctx); /* y^b mod n */
-       BN_mod_mul(y, v, y, rsapar->n, ctx);    /* v^r y^b mod n */
+       BN_mod_exp(v, rsa->q, r, rsa->n, ctx);  /* v^r mod n */
+       BN_mod_exp(y, y, rsa->e, rsa->n, ctx); /* y^b mod n */
+       BN_mod_mul(y, v, y, rsa->n, ctx);       /* v^r y^b mod n */
        temp = BN_cmp(y, g);
        printf("Confirm g^k = v^r y^b mod n: %s\n", temp == 0 ?
            "yes" : "no");
@@ -1026,116 +1061,245 @@ gen_gqkey(
        }
 
        /*
-        * Write the GQ public/private keys as a RSA public key encoded
+        * Write the GQ parameters and keys as a RSA private key encoded
         * in PEM.
+        *
+        * n    modulus n
+        * e    group key b
+        * p    private key u
+        * q    public key (u^-1)^b
+        * (remaining values are not used)
         */
-       str = fheader("GQkey", trustname);
-       PEM_write_RSAPublicKey(str, rsa);
+       str = fheader("GQpar", trustname);
+       pkey = EVP_PKEY_new();
+       EVP_PKEY_assign_RSA(pkey, rsa);
+       PEM_write_PrivateKey(str, pkey, passwd ? EVP_des_cbc() : NULL,
+           NULL, 0, NULL, passwd);
        fclose(str);
        if (debug)
                RSA_print_fp(stdout, rsa, 0);
        fslink(id, trustname);
-       return (rsa);
+       return (pkey);
 }
 
 
 /*
  * Generate Mu-Varadharajan (MV) parameters and keys
  *
- * The Generate Mu-Varadharajan (MV) cryptosystem is intended for use
- * where there is one encryption key for the server and a separate
+ * The Mu-Varadharajan (MV) cryptosystem is intended when servers
+ * broadcast messages to clients, but clients never send messages to
+ * servers. There is one encryption key for the server and a separate
  * decryption key for each client. It operates something like a
- * pay-per-view satellite broadcasting system where the view session key
- * is encrypted by the broadcaster and the decryption keys are held in a
+ * pay-per-view satellite broadcasting system where the session key is
+ * encrypted by the broadcaster and the decryption keys are held in a
  * tamperproof set-top box.
  *
  * The MV parameters and private encryption key hide in a DSA cuckoo
- * structure which uses the same parameters. The values are used in an
- * encryption scheme based on DSA cryptography and a polynomial formed
- * from the expansion of product terms (x - x[j]), as described in: Mu,
- * Y., and V. Varadharajan: Robust and Secure Broadcasting, Proc.
- * Indocrypt 2001, 223-231. The paper has significant errors.
+ * structure which uses the same parameters, but generated in a
+ * different way. The values are used in an encryption scheme similar to
+ * El Gamal cryptography and a polynomial formed from the expansion of
+ * product terms (x - x[j]), as described in Mu, Y., and V.
+ * Varadharajan: Robust and Secure Broadcasting, Proc. Indocrypt 2001,
+ * 223-231. The paper has significant errors and serious omissions.
+ *
+ * Let q be the product of n distinct primes s'[j] (j = 1...n), where
+ * each s'[j] has m significant bits. Let p be a prime p = 2 * q + 1, so
+ * that q and each s'[j] divide p - 1 and p has M = n * m + 1
+ * significant bits. The elements x mod q of Zq with the elements 2 and
+ * the primes removed form a field Zq* valid for polynomial arithetic.
+ * Let g be a generator of Zp; that is, gcd(g, p - 1) = 1 and g^q = 1
+ * mod p. We expect M to be in the 500-bit range and n relatively small,
+ * like 25, so the likelihood of a randomly generated element of x mod q
+ * of Zq colliding with a factor of p - 1 is very small and can be
+ * avoided. Associated with each s'[j] is an element s[j] such that s[j]
+ * s'[j] = s'[j] mod q. We find s[j] as the quotient (q + s'[j]) /
+ * s'[j]. These are the parameters of the scheme and they are expensive
+ * to compute.
+ *
+ * We set up an instance of the scheme as follows. A set of random
+ * values x[j] mod q (j = 1...n), are generated as the zeros of a
+ * polynomial of order n. The product terms (x - x[j]) are expanded to
+ * form coefficients a[i] mod q (i = 0...n) in powers of x. These are
+ * used as exponents of the generator g mod p to generate the private
+ * encryption key A. The pair (gbar, ghat) of public server keys and the
+ * pairs (xbar[j], xhat[j]) (j = 1...n) of private client keys are used
+ * to construct the decryption keys. The devil is in the details.
  *
- * The p is a 512-bit prime, g a generator of Zp and q a 160-bit prime
- * that divides p - 1 and is a qth root of 1 mod p; that is, g^q = 1 mod
- * p. A set of 160-bit values x[j], j = 1...n, are generated as the
- * zeros of a polynomial of order n. The product terms (x - x[j]) are
- * expanded to form coefficients in powers of x[i] mod q, i = 0...n.
- * These are used as exponents of the generator g mod p to generate the
- * private encryption key A. The pair (gbar, ghat) of public values and
- * the pairs (xbar[j], xhat[j]) of private values are used to construct
- * the decryption keys. The devil is in the details.
+ * This routine generates a private encryption file including the
+ * private encryption key E and public key (gbar, ghat). It then
+ * generates decryption files including the private key (xbar[j],
+ * xhat[j]) for each client. E is a permutation that encrypts a block
+ * y = E x. The jth client computes the inverse permutation E^-1 =
+ * gbar^xhat[j] ghat^xbar[j] and decrypts the block x = E^-1 y.
  *
- * This routine generates an encryption file including the prime modulus
- * p, encryption key A and public key (gbar, ghat). It then generates 
- * decryption files including the prime modulus, public key and private
- * key (xbar[j], xhat[j]) for each client. The server encrypts a block
- * y = A^x; the jth client decrypts x = (gbar^xhat[j] ghat^xbar[j])^y.
+ * The distinguishing characteristic of this scheme is the capability to
+ * revoke keys. Included in the calculation of E, gbar and ghat is the
+ * product s = prod(s'[j]) (j = 1...n) above. If the factor s'[j] is
+ * subsequently removed from the product and E, gbar and ghat
+ * recomputed, the jth client will no longer be able to compute E^-1 and
+ * thus unable to decrypt the block.
  */
-void
-gen_mv(
+EVP_PKEY *                     /* DSA cuckoo nest */
+gen_mvpar(
        char    *id             /* file name id */
        )
 {
+       EVP_PKEY *pkey;         /* private key */
        DSA     *dsa;           /* DSA parameters */
+       DSA     *sdsa;          /* DSA parameters */
        BN_CTX  *ctx;           /* BN working space */
-       BIGNUM  **x;            /* private key vector */
-       BIGNUM  **a;            /* coefficient vector */
+       BIGNUM  **x;            /* polynomial zeros vector */
+       BIGNUM  **a;            /* polynomial coefficient vector */
        BIGNUM  **g;            /* public key vector */
-       BIGNUM  **s;            /* private enabling keys */
-       BIGNUM  **xbar;         /* private key vector 1 */
-       BIGNUM  **xhat;         /* private key vector 2 */
+       BIGNUM  **s, **s1;      /* private enabling keys */
+       BIGNUM  **xbar, **xhat; /* private keys vector */
        BIGNUM  *b;             /* group key */
-       BIGNUM  *binverse;      /* inverse group key */
+       BIGNUM  *b1;            /* inverse group key */
+       BIGNUM  *ss;            /* enabling key */
        BIGNUM  *biga;          /* master encryption key */
        BIGNUM  *bige;          /* session encryption key */
-       BIGNUM  *k;             /* random roll */
-       BIGNUM  *ss;            /* enabling key */
-       BIGNUM  *gbar;          /* public key 1 */
-       BIGNUM  *ghat;          /* public key 2 */
+       BIGNUM  *gbar, *ghat;   /* public key */
        BIGNUM  *u, *v, *w;     /* BN scratch */
-       u_char  seed[20];       /* seed for parameters */
        int     i, j, n;
        FILE    *str;
        u_int   temp;
        char    ident[20];
 
        /*
-        * Generate DSA parameters for use as MV parameters.
+        * Generate MV parameters.
+        *
+        * The object is to generate a multiplicative group Zp mod p and
+        * a subset Zq mod q, where q is the product of n distinct
+        * primes s'[j] (j = 1...n) and q divides p - 1. We first
+        * generate n distinct primes, which may have to be regenerated
+        * later. As a practical matter, it is tough to find more than
+        * 31 distinct primes for modulus 512 or 61 primes for modulus
+        * 1024. The latter can take several hundred iterations and
+        * several minutes on a Blade 1000.
         */
-       printf("Generating MV parameters (%d bits)...\n", modulus);
        n = nkeys;
-       RAND_bytes(seed, sizeof(seed));
-       dsa = DSA_generate_parameters(modulus, seed, sizeof(seed),
-           NULL, NULL, cb, "MV");
-       printf("\n");
-       if (dsa == NULL) {
-               printf("MV generate parameters fails\n%s\n",
-                   ERR_error_string(ERR_get_error(), NULL));
-               rval = -1;
-               return;
+       printf("Generating MV parameters for %d keys (%d bits)...\n", n,
+           modulus / n);
+       ctx = BN_CTX_new(); u = BN_new(); v = BN_new(); w = BN_new();
+       b = BN_new(); b1 = BN_new();
+       dsa = malloc(sizeof(DSA));
+       dsa->p = BN_new();
+       dsa->q = BN_new();
+       dsa->g = BN_new();
+       s = malloc((n + 1) * sizeof(BIGNUM));
+       s1 = malloc((n + 1) * sizeof(BIGNUM));
+       for (j = 1; j <= n; j++)
+               s1[j] = BN_new();
+       temp = 0;
+       for (j = 1; j <= n; j++) {
+               while (1) {
+                       printf("Birthdays %d\r", temp);
+                       BN_generate_prime(s1[j], modulus / n, 0, NULL,
+                           NULL, NULL, NULL);
+                       for (i = 1; i < j; i++) {
+                               if (BN_cmp(s1[i], s1[j]) == 0)
+                                       break;
+                       }
+                       if (i == j)
+                               break;
+                       temp++;
+               }
        }
+       printf("Birthday keys rejected %d\n", temp);
 
        /*
-        * Generate random polynomial roots mod q.
+        * Compute the modulus q as the product of the primes. Compute
+        * the modulus p as 2 * q + 1 and test p for primality. If p
+        * is composite, replace one of the primes with a new distinct
+        * one and try again. Note that q will hardly be a secret since
+        * we have to reveal p to servers and clients. However,
+        * factoring q to find the primes should be adequately hard, as
+        * this is the same problem considered hard in RSA.
         */
-       printf("Generating polynomial roots (%d bits)...\n",
-           BN_num_bits(dsa->q));
-       ctx = BN_CTX_new(); u = BN_new(); v = BN_new(); w = BN_new();
-       n = nkeys;
+       temp = 0;
+       while (1) {
+               printf("Duplicate keys rejected %d\r", ++temp);
+               BN_one(dsa->q);
+               for (j = 1; j <= n; j++)
+                       BN_mul(dsa->q, dsa->q, s1[j], ctx);
+               BN_copy(dsa->p, dsa->q);
+               BN_add(dsa->p, dsa->p, dsa->p);
+               BN_add_word(dsa->p, 1);
+               if (BN_is_prime(dsa->p, BN_prime_checks, NULL, ctx,
+                   NULL))
+                       break;
+
+               j = temp % n + 1;
+               while (1) {
+                       BN_generate_prime(u, modulus / n, 0, 0, NULL,
+                           NULL, NULL);
+                       for (i = 1; i <= n; i++) {
+                               if (BN_cmp(u, s1[i]) == 0)
+                                       break;
+                       }
+                       if (i > n)
+                               break;
+               }
+               BN_copy(s1[j], u);
+       }
+       printf("Duplicate keys rejected %d\n", temp);
+
+       /*
+        * Compute the generator g using a random roll such that
+        * gcd(g, p - 1) = 1 and g^q = 1.
+        */
+       BN_copy(v, dsa->p);
+       BN_sub_word(v, 1);
+       while (1) {
+               BN_rand(dsa->g, BN_num_bits(dsa->p) - 1, 0, 0);
+               BN_mod(dsa->g, dsa->g, dsa->p, ctx);
+               BN_gcd(u, dsa->g, v, ctx);
+               if (!BN_is_one(u))
+                       continue;
+
+               BN_mod_exp(u, dsa->g, dsa->q, dsa->p, ctx);
+               if (BN_is_one(u))
+                       break;
+       }
+
+       /*
+        * Compute s[j] such that s[j] * s'[j] = s'[j] for all j. The
+        * easy way to do this is to compute q + s'[j] and divide the
+        * result by s'[j]. Exercise for the student: prove the
+        * remainder is always zero.
+        */
+       for (j = 1; j <= n; j++) {
+               s[j] = BN_new();
+               BN_add(s[j], dsa->q, s1[j]);
+               BN_div(s[j], u, s[j], s1[j], ctx);
+       }
+
+       /*
+        * Setup is now complete. Roll random polynomial roots x[j]
+        * (0 < x[j] < q) for all j. While it may not be strictly
+        * necessary, Make sure each root has no factors in common with
+        * q.
+        */
+       printf(
+           "Generating polynomial coefficients for %d roots (%d bits)\n",
+           n, BN_num_bits(dsa->q)); 
        x = malloc((n + 1) * sizeof(BIGNUM));
        for (j = 1; j <= n; j++) {
                x[j] = BN_new();
-               BN_rand(x[j], BN_num_bits(dsa->q), -1, 0);
-               BN_mod(x[j], x[j], dsa->q, ctx);
+               while (1) {
+                       BN_rand(x[j], BN_num_bits(dsa->q), 0, 0);
+                       BN_mod(x[j], x[j], dsa->q, ctx);
+                       BN_gcd(u, x[j], dsa->q, ctx);
+                       if (BN_is_one(u))
+                               break;
+               }
        }
 
        /*
-        * Generate polynomial coefficients a[i], i = 0...n, from the
-        * expansion of root products (x - x[j]), j = 1...n. The method
-        * is a present from Charlie Boncelet.
+        * Generate polynomial coefficients a[i] (i = 0...n) from the
+        * expansion of root products (x - x[j]) mod q for all j. The
+        * method is a present from Charlie Boncelet.
         */
-       printf("Generating polynomial coefficients for %d keys\n", n); 
        a = malloc((n + 1) * sizeof(BIGNUM));
        for (i = 0; i <= n; i++) {
                a[i] = BN_new();
@@ -1144,49 +1308,30 @@ gen_mv(
        for (j = 1; j <= n; j++) {
                BN_zero(w);
                for (i = 0; i < j; i++) {
-                       u = BN_dup(dsa->q);
+                       BN_copy(u, dsa->q);
                        BN_mod_mul(v, a[i], x[j], dsa->q, ctx);
                        BN_sub(u, u, v);
                        BN_add(u, u, w);
-                       w = BN_dup(a[i]);
+                       BN_copy(w, a[i]);
                        BN_mod(a[i], u, dsa->q, ctx);
                }
        }
 
        /*
-        * Verify sum(a[i] x^i) = 0 for all j. By design, the polynomial
-        * has n zeros at x = x[j].
-        */
-       temp = 1;
-       for (j = 1; j <= n; j++) {
-               BN_zero(u);
-               for (i = 0; i <= n; i++) {
-                       BN_set_word(v, i);
-                       BN_mod_exp(v, x[j], v, dsa->q, ctx);
-                       BN_mod_mul(v, v, a[i], dsa->q, ctx);
-                       BN_add(u, u, v);
-               }
-               BN_mod(u, u, dsa->q, ctx);
-               if (!BN_is_zero(u))
-                       temp = 0;
-       }
-       printf("Confirm sum(a[i] x[j]^i) = 0 for all i, j: %s\n", temp ?
-           "yes" : "no");
-
-       /*
-        * Generate g[i] = g^a[i] for all i and the generator g.
+        * Generate g[i] = g^a[i] mod p for all i and the generator g.
         */
        printf("Generating g[i] parameters\n");
        g = malloc((n + 1) * sizeof(BIGNUM));
        for (i = 0; i <= n; i++) {
                g[i] = BN_new();
-               BN_mod_exp(g[i], dsa->g, a[i], dsa->p,
-                   ctx);
+               BN_mod_exp(g[i], dsa->g, a[i], dsa->p, ctx);
        }
 
        /*
-        * Verify prod(g[i]^(x[j]^i)) = 1 for all i, j. Note the
-        * expression given in the paper is incorrect.
+        * Verify prod(g[i]^(a[i] x[j]^i)) = 1 for all i, j; otherwise,
+        * exit. Note the a[i] x[j]^i exponent is computed mod q, but
+        * the g[i] is computed mod p. also note the expression given in
+        * the paper is incorrect.
         */
        temp = 1;
        for (j = 1; j <= n; j++) {
@@ -1194,7 +1339,8 @@ gen_mv(
                for (i = 0; i <= n; i++) {
                        BN_set_word(v, i);
                        BN_mod_exp(v, x[j], v, dsa->q, ctx);
-                       BN_mod_exp(v, g[i], v, dsa->p, ctx);
+                       BN_mod_mul(v, v, a[i], dsa->q, ctx);
+                       BN_mod_exp(v, dsa->g, v, dsa->p, ctx);
                        BN_mod_mul(u, u, v, dsa->p, ctx);
                }
                if (!BN_is_one(u))
@@ -1202,11 +1348,14 @@ gen_mv(
        }
        printf("Confirm prod(g[i]^(x[j]^i)) = 1 for all i, j: %s\n",
            temp ? "yes" : "no");
+       if (!temp) {
+               rval = -1;
+               return (NULL);
+       }
 
        /*
-        * Make 512-bit encryption key A and 160-bit nonce pair b and
-        * b^-1. Keep A around for awhile, since it is expensive to
-        * compute.
+        * Make private encryption key A. Keep it around for awhile,
+        * since it is expensive to compute.
         */
        biga = BN_new();
        BN_one(biga);
@@ -1218,30 +1367,32 @@ gen_mv(
                        BN_mod_mul(biga, biga, v, dsa->p, ctx);
                }
        }
-       b = BN_new(); binverse = BN_new();
-       BN_rand(b, BN_num_bits(dsa->q), -1, 0);
-       BN_mod(b, b, dsa->q, ctx);
-       BN_mod_inverse(binverse, b, dsa->q, ctx);
-       BN_mod_mul(v, b, binverse, dsa->q, ctx);
-       printf("Confirm b b^-1 = 1: %s\n", BN_is_one(v) ?
-           "yes" : "no");
 
        /*
-        * Make 160-bit decryption keys (xbar[j], xhat[j]) for all j.
-        * Also make 512-bit s[j] = r q for some r and prod(s[j]), both
-        * mod p.
+        * Roll private random group key b mod q (0 < b < q), where
+        * gcd(b, q) = 1 to guarantee the b^1 exists, then compute
+        * b^-1 mod q. If b is changed, the client keys must be
+        * recomputed.
+        */
+       while (1) {
+               BN_rand(b, BN_num_bits(dsa->q), 0, 0);
+               BN_mod(b, b, dsa->q, ctx);
+               BN_gcd(u, b, dsa->q, ctx);
+               if (BN_is_one(u))
+                       break;
+       }
+       BN_mod_inverse(b1, b, dsa->q, ctx);
+
+       /*
+        * Make private client keys (xbar[j], xhat[j]) for all j. Note
+        * that the keys for the jth client involve s[j], but not s'[j]
+        * or the product s = prod(s'[j]) mod q, which is the enabling
+        * key.
         */
        xbar = malloc((n + 1) * sizeof(BIGNUM));
        xhat = malloc((n + 1) * sizeof(BIGNUM));
-       s = malloc((n + 1) * sizeof(BIGNUM));
-       ss = BN_new();
-       BN_set_word(ss, 1);
        for (j = 1; j <= n; j++) {
-               xbar[j] = BN_new(); xhat[j] = BN_new(); s[j] = BN_new();
-               BN_set_word(u, j);
-               BN_mod_mul(s[j], u, dsa->q, dsa->p, ctx);
-               BN_add_word(s[j], 1);
-               BN_mod_mul(ss, ss, s[j], dsa->p, ctx);
+               xbar[j] = BN_new(); xhat[j] = BN_new();
                BN_zero(xbar[j]);
                BN_set_word(v, n);
                for (i = 1; i <= n; i++) {
@@ -1250,88 +1401,82 @@ gen_mv(
                        BN_mod_exp(u, x[i], v, dsa->q, ctx);
                        BN_add(xbar[j], xbar[j], u);
                }
-               BN_mod_mul(xbar[j], xbar[j], binverse, dsa->q, ctx);
+               BN_mod_mul(xbar[j], xbar[j], b1, dsa->q, ctx);
                BN_mod_exp(xhat[j], x[j], v, dsa->q, ctx);
-               BN_mod_mul(xhat[j], xhat[j], s[j], dsa->p, ctx);
+               BN_mod_mul(xhat[j], xhat[j], s[j], dsa->q, ctx);
        }
 
        /*
-        * Verify A^s g^(s b xbar[j]) g^(s xhat[j]) = 1 for all j.
+        * The enabling key is initially q by construction. We can
+        * revoke client j by dividing q by s'[j]. The quotient becomes
+        * the enabling key s. Note we always have to revoke one key;
+        * otherwise, the plaintext and cryptotext would be identical.
         */
-       temp = 1;
-       for (j = 1; j <= n; j++) {
-               BN_mod_mul(u, ss, b, dsa->q, ctx);
-               BN_mod_mul(u, u, xbar[j], dsa->q, ctx);
-               BN_mod_exp(u, dsa->g, u, dsa->p, ctx);
-               BN_mod_mul(v, ss, xhat[j], dsa->q, ctx);
-               BN_mod_exp(v, dsa->g, v, dsa->p, ctx);
-               BN_mod_mul(u, u, v, dsa->p, ctx);
-               BN_mod_exp(v, biga, ss, dsa->p, ctx);
-               BN_mod_mul(u, u, v, dsa->p, ctx);
-               if (!BN_is_one(u))
-                       temp = 0;
-       }
-       printf("Confirm A^s g^(s b xbar[j]) g^(s xhat[j]) = 1 for all j: %s\n",
-           temp ? "yes" : "no");
+       ss = BN_new();
+       BN_copy(ss, dsa->q);
+       BN_div(ss, u, dsa->q, s1[n], ctx);
 
        /*
-        * Make 512-bit values E = A^(s k), gbar = g^(s k) and
-        * ghat = g^(s k b).
+        * Make private server encryption key E = A^s and public server
+        * keys gbar = g^s mod p and ghat = g^(s b) mod p. The (gbar,
+        * ghat) is the public key provided to the server, which uses it
+        * to compute the session encryption key and public key included
+        * in its messages. These values must be regenerated if the
+        * enabling key is changed.
         */
-       bige = BN_new(); k = BN_new();
-       BN_rand(k, BN_num_bits(dsa->q), -1, 0);
-       BN_mod(k, k, dsa->q, ctx);
-       BN_mod_mul(v, ss, k, dsa->q, ctx);
-       BN_mod_exp(bige, biga, v, dsa->p, ctx);
-       gbar = BN_new(); ghat = BN_new();
-       BN_mod_exp(gbar, dsa->g, v, dsa->p, ctx);
-       BN_mod_mul(v, v, b, dsa->q, ctx);
+       bige = BN_new(); gbar = BN_new(); ghat = BN_new();
+       BN_mod_exp(bige, biga, ss, dsa->p, ctx);
+       BN_mod_exp(gbar, dsa->g, ss, dsa->p, ctx);
+       BN_mod_mul(v, ss, b, dsa->q, ctx);
        BN_mod_exp(ghat, dsa->g, v, dsa->p, ctx);
 
        /*
-        * Verify E gbar^xbar[j] ghat^xhat[j] = 1 for all j.
-        */
-       temp = 1;
-       for (j = 1; j <= n; j++) {
-               BN_mod_exp(v, gbar, xhat[j], dsa->p, ctx);
-               BN_mod_exp(u, ghat, xbar[j], dsa->p, ctx);
-               BN_mod_mul(u, u, v, dsa->p, ctx);
-               BN_mod_mul(u, bige, u, dsa->p, ctx);
-
-               BN_mod_inverse(v, u, dsa->p, ctx);
-               if (!BN_is_one(u)) {
-                       printf("revoke %i\n", j);
-                       temp = 0;
-               }
-       }
-       printf(
-           "Confirm A^(s k) gbar^xbar[j] ghat^xhat[j] = 1 for all j: %s\n",
-           temp ? "yes" : "no");
-
-       /*
-        * We now have the modulus p, encryption key A, public key
-        * (gbar^k, ghat^bk) and a set of decryption keys (xbar[j],
-        * xhat[j]) for all j. The broadcaster parameters and keys are
-        * contained in a DSA cuckoo structure:
+        * We produce the key media in three steps. The first step is to
+        * generate the private values that do not depend on the
+        * enabling key. These include the server values p, q, g, b, A
+        * and the client values s'[j], xbar[j] and xhat[j] for each j.
+        * The p, xbar[j] and xhat[j] values are encoded in private
+        * files which are distributed to respective clients. The p, q,
+        * g, A and s'[j] values (will be) written to a secret file to
+        * be read back later.
+        *
+        * The secret file (will be) read back at some later time to
+        * enable/disable individual keys and generate/regenerate the
+        * enabling key s. The p, q, E, gbar and ghat values are written
+        * to a secret file to be read back later by the server.
         *
-        * p            modulus p
-        * q            private encryption key A
-        * g            enabling key ss
-        * priv_key     public key 1 gbar
-        * pub_key      public key 2 ghat
+        * The server reads the private file and rolls the session key
+        * k, then computes E^k, gbar^k and ghat^k. The E^k is the new
+        * symmetric key which is installed in the key cache. The gbar^k
+        * and ghat^k values are transmtted to clients in an extension
+        * field.
+        *
+        * The client receives the message and computes x =
+        * (gbar^k)^xbar[j] (ghat^k)^xhat[j], finds the encryption key
+        * E^k as the inverse x^-1 of x and installs in the key cache.
+        * Once installed, the crypto computations don't have to be done
+        * again until the session key is refreshed, expected to be done
+        * once per day.
         */
-       BN_copy(dsa->q, bige);
-       BN_copy(dsa->g, ss);
+       BN_copy(dsa->g, bige);
        dsa->priv_key = BN_dup(gbar);
        dsa->pub_key = BN_dup(ghat);
 
        /*
-        * Write the parameters and public key as a DSA private key
-        * encoded in PEM. This is used only by the broadcaster(s).
+        * Write the MV server parameters and keys as a DSA private key
+        * encoded in PEM.
+        *
+        * p    modulus p
+        * q    modulus q (used only to generate k)
+        * g    E mod p
+        * priv_key gbar mod p
+        * pub_key ghat mod p
         */
-       str = fheader("MVkey", trustname);
-       PEM_write_DSAPrivateKey(str, dsa, passwd ? EVP_des_cbc() :
-           NULL, NULL, 0, NULL, passwd);
+       str = fheader("MVpar", trustname);
+       pkey = EVP_PKEY_new();
+       EVP_PKEY_assign_DSA(pkey, dsa);
+       PEM_write_PrivateKey(str, pkey, passwd ? EVP_des_cbc() : NULL,
+           NULL, 0, NULL, passwd);
        fclose(str);
        if (debug)
                DSA_print_fp(stdout, dsa, 0);
@@ -1341,57 +1486,66 @@ gen_mv(
         * Write the parameters and private key (xbar[j], xhat[j]) for
         * all j as a DSA private key encoded in PEM. It is used only by
         * the designated recipient(s) who pay a suitably outrageous fee
-        * for the service.
-        *
-        * The receiver parameters and keys are contained in the DSA
-        * cuckoo structure:
-        *
-        * p            modulus p
-        * q            private key 1 xbar[j]
-        * g            private key 2 xhat[j]
-        * priv_key     public key 1 gbar
-        * pub_key      public key 2 ghat
+        * for its use.
         */
+       sdsa = malloc(sizeof(DSA));
+       sdsa->p = BN_dup(dsa->p);
+       sdsa->q = BN_dup(BN_value_one());
+       sdsa->g = BN_dup(BN_value_one());
+       sdsa->priv_key = BN_new();
+       sdsa->pub_key = BN_new();
        for (j = 1; j <= n; j++) {
-               BN_copy(dsa->q, xbar[j]);
-               BN_copy(dsa->g, xhat[j]);
+               BN_copy(sdsa->priv_key, xbar[j]);
+               BN_copy(sdsa->pub_key, xhat[j]);
+               BN_mod_exp(v, dsa->priv_key, sdsa->pub_key, dsa->p,
+                   ctx);
+               BN_mod_exp(u, dsa->pub_key, sdsa->priv_key, dsa->p,
+                   ctx);
+               BN_mod_mul(u, u, v, dsa->p, ctx);
+               BN_mod_mul(u, u, dsa->g, dsa->p, ctx);
+               if (!BN_is_one(u))
+                       printf("Revoke key %d\n", j);
                BN_free(xbar[j]); BN_free(xhat[j]);
-               BN_free(x[j]); BN_free(s[j]);
+               BN_free(x[j]); BN_free(s[j]); BN_free(s1[j]);
 
                /*
-                * Write the MV public key as a DSA private key encoded
-                * in PEM. In this context the public key is really
-                * public only to the designated recipients and denied
-                * to all others.
+                * Write the client parameters as a DSA private key
+                * encoded in PEM. We don't make links for these.
+                *
+                * p    modulus p
+                * priv_key xbar[j] mod q
+                * pub_key xhat[j] mod q
+                * (remaining values are not used)
                 */
-#if 0
                sprintf(ident, "MVkey%d", j);
                str = fheader(ident, trustname);
-               PEM_write_DSAPrivateKey(str, dsa, passwd ?
-                   EVP_des_cbc() : NULL, NULL, 0, NULL, passwd);
+               pkey = EVP_PKEY_new();
+               EVP_PKEY_assign_DSA(pkey, sdsa);
+               PEM_write_PrivateKey(str, pkey, passwd ? EVP_des_cbc() :
+                   NULL, NULL, 0, NULL, passwd);
                fclose(str);
                if (debug)
-                       DSA_print_fp(stdout, dsa, 0);
-#endif
+                       DSA_print_fp(stdout, sdsa, 0);
        }
 
        /*
-        * Free the coutries.
+        * Free the countries.
         */
        for (i = 0; i <= n; i++) {
                BN_free(a[i]);
                BN_free(g[i]);
        }
        BN_free(u); BN_free(v); BN_free(w); BN_CTX_free(ctx);
-       BN_free(b); BN_free(binverse); BN_free(biga); BN_free(bige);
-       BN_free(k); BN_free(ss); BN_free(gbar); BN_free(ghat);
+       BN_free(b); BN_free(b1); BN_free(biga); BN_free(bige);
+       BN_free(ss); BN_free(gbar); BN_free(ghat);
+       DSA_free(dsa); DSA_free(sdsa);
 
        /*
         * Free the world.
         */
-       free(x); free(a); free(g); free(s); free(xbar); free(xhat);
-       DSA_free(dsa);
-       return;
+       free(x); free(a); free(g); free(s); free(s1);
+       free(xbar); free(xhat);
+       return (pkey);
 }
 
 
@@ -1488,7 +1642,7 @@ x509      (
        }
        X509_EXTENSION_free(ex);
        /*
-        * The subject_key_identifier is used for the GQ group key.
+        * The subject_key_identifier is used for the GQ public key.
         * This should not be controversial.
         */
        if (gqpub != NULL) {