diff -Npru src.orig/config/general.h src/config/general.h
--- src.orig/config/general.h	2010-06-29 15:31:33.000000000 -0400
+++ src/config/general.h	2010-07-09 19:51:21.000000000 -0400
@@ -55,7 +55,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
 
 #define	DOWNLOAD_PROTO_TFTP	/* Trivial File Transfer Protocol */
 #define	DOWNLOAD_PROTO_HTTP	/* Hypertext Transfer Protocol */
-#undef	DOWNLOAD_PROTO_HTTPS	/* Secure Hypertext Transfer Protocol */
+#define	DOWNLOAD_PROTO_HTTPS	/* Secure Hypertext Transfer Protocol */
 #undef	DOWNLOAD_PROTO_FTP	/* File Transfer Protocol */
 #undef	DOWNLOAD_PROTO_TFTM	/* Multicast Trivial File Transfer Protocol */
 #undef	DOWNLOAD_PROTO_SLAM	/* Scalable Local Area Multicast */
diff -Npru src.orig/crypto/x509.c src/crypto/x509.c
--- src.orig/crypto/x509.c	2010-06-29 15:31:33.000000000 -0400
+++ src/crypto/x509.c	2010-07-09 19:51:21.000000000 -0400
@@ -22,6 +22,13 @@ FILE_LICENCE ( GPL2_OR_LATER );
 #include <string.h>
 #include <errno.h>
 #include <gpxe/asn1.h>
+#include <gpxe/base64.h>
+#include <gpxe/crypto.h>
+#include <gpxe/image.h>
+#include <gpxe/io.h>
+#include <gpxe/md5.h>
+#include <gpxe/settings.h>
+#include <gpxe/sha1.h>
 #include <gpxe/x509.h>
 
 /** @file
@@ -37,6 +44,25 @@ FILE_LICENCE ( GPL2_OR_LATER );
 static const uint8_t oid_rsa_encryption[] = { 0x2a, 0x86, 0x48, 0x86, 0xf7,
 					      0x0d, 0x01, 0x01, 0x01 };
 
+/** Object Identifier for "md5" (1.2.840.113549.2.5) */
+static const uint8_t oid_md5_digest[] = { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x02,
+					  0x05 };
+
+/** Object Identifier for "id-sha1" (1.3.14.3.2.26) */
+static const uint8_t oid_sha1_digest[] = { 0x2b, 0x0e, 0x03, 0x02, 0x1a };
+
+/** Object Identifier for "md5WithRSAEncryption" (1.2.840.113549.1.1.4) */
+static const uint8_t oid_md5_rsa_signature[] = { 0x2a, 0x86, 0x48, 0x86, 0xf7,
+						 0x0d, 0x01, 0x01, 0x04 };
+
+/** Object Identifier for "sha-1WithRSAEncryption" (1.2.840.113549.1.1.5) */
+static const uint8_t oid_sha1_rsa_signature[] = { 0x2a, 0x86, 0x48, 0x86,
+						  0xf7, 0x0d, 0x01, 0x01,
+						  0x05 };
+
+/** Object Identifier for the subject CN attribute */
+static const uint8_t oid_cn_attribute[] = { 0x55, 0x04, 0x03 };
+
 /**
  * Identify X.509 certificate public key
  *
@@ -91,10 +117,611 @@ static int x509_public_key ( const struc
 }
 
 /**
+ * Ensure this certificate's common name matches what is expected
+ *
+ * @v subject		Cursor in the start of the subject sequence
+ * @ret rc		Return status code
+ *
+ * An HTTPS connection expects the host name by default.  A secondary expected
+ * name can be stored in "x509.service", allowing the user to potentially
+ * specify the value in a script for situations without DNS etc.
+ *
+ * FIXME: This should probably check extended values as well.
+ */
+static int x509_verify_service ( const struct asn1_cursor *subject ) {
+	struct asn1_cursor cn;
+	struct asn1_cursor oid;
+	struct asn1_cursor rdn_attr;
+	char buf[256];
+	int rc;
+
+	/* Locate common name in the sequence of subject attributes */
+	memcpy ( &rdn_attr, subject, sizeof ( rdn_attr ) );
+	do {
+		/* Enter the current attribute set */
+		memcpy ( &cn, &rdn_attr, sizeof ( cn ) );
+		rc = ( asn1_enter ( &cn, ASN1_SET ), /* attribute */
+		       asn1_enter ( &cn, ASN1_SEQUENCE ) /* type+val */ );
+		if ( rc != 0 )
+			break;
+
+		/* Test if the OID specifies this set is a common name */
+		memcpy ( &oid, &cn, sizeof ( oid ) );
+		rc = ( asn1_enter ( &oid, ASN1_OID ) /* type */ );
+		if ( rc != 0 )
+			break;
+		if ( oid.len == sizeof ( oid_cn_attribute ) &&
+		     memcmp ( oid.data, oid_cn_attribute, oid.len ) != 0 )
+			continue;
+
+		/* Try to enter the CN value as a UTF8 string */
+		memcpy ( &oid, &cn, sizeof ( oid ) ); /* Store a backup copy */
+		rc = ( asn1_skip ( &cn, ASN1_OID ),
+		       asn1_enter ( &cn, ASN1_UTF8_STRING ) );
+
+		/* Try to enter the CN value as a printable string */
+		if ( rc != 0 ) {
+			memcpy ( &cn, &oid, sizeof ( cn ) ); /* Fall back */
+			rc = ( asn1_skip ( &cn, ASN1_OID ),
+			       asn1_enter ( &cn, ASN1_PRINTABLE_STRING ) );
+		}
+
+		break;
+	} while ( asn1_skip ( &rdn_attr, ASN1_SET ) == 0 );
+	if ( rc != 0 || rdn_attr.data == NULL ) { /* Error or out of attrs */
+		DBG ( "Cannot locate common name attribute in:\n" );
+		DBG_HDA ( 0, subject->data, subject->len );
+		return rc ? rc : -EILSEQ;
+	}
+
+	/* Retrieve the URI host name as a possible expected common name */
+	rc = fetchf_named_setting ( "x509.host", buf, sizeof ( buf ) );
+	if ( rc < 0 )
+		DBG ( "Missing host name for the expected x509 subject\n" );
+
+	/* Compare the expected (host) name against given name */
+	if ( rc != (signed)cn.len || memcmp ( buf, cn.data, rc ) != 0 ) {
+
+		/* Retrieve the user-defined trusted common name */
+		rc = fetchf_named_setting ( "x509.service",
+					    buf, sizeof ( buf ) );
+		if ( rc < 0 )
+			DBG ( "There is no user-defined x509 common name\n" );
+
+		/* Compare the expected (user-set) name against given name */
+		if ( rc != (signed)cn.len ||
+		     memcmp ( buf, cn.data, rc ) != 0 ) {
+			DBG ( "Unexpected common name found in:\n" );
+			DBG_HDA ( 0, subject->data, subject->len );
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * Extract a GeneralTime string from the given cursor position
+ *
+ * @v time		Cursor pointing at a timestamp type
+ * @v date_string	An allocated 16-char array to be filled with the date
+ * @v old_format	To be filled with the original format (UTC or GENERAL)
+ * @ret rc		Return status code
+ *
+ * RFC5280's limitations on ASN.1 timestamps dictate they must be either
+ * YYMMDDHHMMSSZ (pre-2050) or YYYYMMDDHHMMSSZ (2050 or later).  We convert to
+ * the less-ambiguous GeneralTime for easy strcmp usage.
+ */
+static int x509_get_general_time ( const struct asn1_cursor *time,
+				   char date_string[16], int *old_format ) {
+	struct asn1_cursor time_tmp;
+	int rc;
+
+	/* If it is GeneralTime already, just make a null-terminated string. */
+	memcpy ( &time_tmp, time, sizeof ( time_tmp ) );
+	rc = ( asn1_enter ( &time_tmp, ASN1_GENERAL_TIME ) );
+	if ( rc == 0 && time_tmp.len == 15 ) {
+		memcpy ( date_string, time_tmp.data, 15 );
+		*old_format = ASN1_GENERAL_TIME;
+		date_string[15] = '\0';
+		return 0;
+	}
+
+	/* Convert UTCTime to GeneralTime in a null-terminated string. */
+	memcpy ( &time_tmp, time, sizeof ( time_tmp ) );
+	rc = ( asn1_enter ( &time_tmp, ASN1_UTC_TIME ) );
+	if ( rc == 0 && time_tmp.len == 13 ) {
+		memcpy ( date_string + 2, time_tmp.data, 13 );
+		*old_format = ASN1_UTC_TIME;
+		if ( date_string[2] >= '5' ) {
+			date_string[0] = '1';
+			date_string[1] = '9';
+		} else {
+			date_string[0] = '2';
+			date_string[1] = '0';
+		}
+		date_string[15] = '\0';
+		return 0;
+	}
+
+	/* FIXME: Maybe add character-level validation for the new string */
+	return -EILSEQ;
+}
+
+/**
+ * Create a GeneralTime string with the current time
+ *
+ * @v date_string	An allocated 16-char array to be filled with the date
+ * @ret rc		Return status code
+ *
+ * FIXME: This is a HACK to read a current timestamp from CMOS.
+ */
+static int x509_current_general_time ( char date_string[16] ) {
+#if defined ( __x86_64__ ) || defined ( __i386__ )
+	unsigned char buf[10];
+	unsigned char i;
+
+	/* Read the first ten bytes from CMOS */
+	for ( i=0x00; i<0x0A; i++ ) {
+		outb ( i, 0x70 );
+		buf[i] = inb ( 0x71 );
+	}
+	outb ( 0x32, 0x70 );
+	buf[1] = inb ( 0x71 ); /* Overwrite an alarm byte with the century */
+
+	/* Convert from BCD bytes to a real string */
+	date_string[ 0] = '0' + buf[1] / 16;
+	date_string[ 1] = '0' + buf[1] % 16;
+	date_string[ 2] = '0' + buf[9] / 16;
+	date_string[ 3] = '0' + buf[9] % 16;
+	date_string[ 4] = '0' + buf[8] / 16;
+	date_string[ 5] = '0' + buf[8] % 16;
+	date_string[ 6] = '0' + buf[7] / 16;
+	date_string[ 7] = '0' + buf[7] % 16;
+	date_string[ 8] = '0' + buf[4] / 16;
+	date_string[ 9] = '0' + buf[4] % 16;
+	date_string[10] = '0' + buf[2] / 16;
+	date_string[11] = '0' + buf[2] % 16;
+	date_string[12] = '0' + buf[0] / 16;
+	date_string[13] = '0' + buf[0] % 16;
+	date_string[14] = 'Z'; /* CMOS RTC should be GMT (sans Windows) */
+	date_string[15] = '\0';
+
+	return 0;
+#else
+	/* If the build is not for an x86 machine, we assume they don't follow
+	 * any BIOS/CMOS conventions.  This non-zero error code will cause the
+	 * test of validity times to be skipped, rather than always fail and
+	 * throw errors when using TLS on such hardware.
+	 */
+	return -ENOSYS;
+#endif
+}
+
+/**
+ * Test if a given timestamp falls within a certificate's validity period
+ *
+ * @v certificate	Certificate
+ * @v general_time	The time in GeneralTime format to be tested
+ * @ret rc		Return status code
+ */
+static int x509_is_valid_time ( const struct asn1_cursor *certificate,
+				char general_time[16] ) {
+	struct asn1_cursor validity;
+	char valid_date[16];
+	int format;
+	int rc;
+
+	/* Locate validity */
+	memcpy ( &validity, certificate, sizeof ( validity ) );
+	rc = ( asn1_enter ( &validity, ASN1_SEQUENCE ), /* Certificate */
+	       asn1_enter ( &validity, ASN1_SEQUENCE ), /* tbsCertificate */
+	       asn1_skip ( &validity, ASN1_EXPLICIT_TAG ), /* version */
+	       asn1_skip ( &validity, ASN1_INTEGER ), /* serialNumber */
+	       asn1_skip ( &validity, ASN1_SEQUENCE ), /* signature */
+	       asn1_skip ( &validity, ASN1_SEQUENCE ), /* issuer */
+	       asn1_enter ( &validity, ASN1_SEQUENCE ) /* validity */ );
+	if ( rc != 0 ) {
+		DBG ( "Cannot locate validity in:\n" );
+		DBG_HDA ( 0, certificate->data, certificate->len );
+		return rc;
+	}
+
+	/* Normalize the lower bound and test it against the given stamp */
+	rc = x509_get_general_time ( &validity, valid_date, &format );
+	if ( rc != 0 ) {
+		DBG ( "Cannot locate notBefore timestamp:\n" );
+		DBG_HDA ( 0, certificate->data, certificate->len );
+		return rc;
+	}
+	if ( strcmp ( general_time, valid_date ) < 0 ) {
+		DBG ( "The certificate is not yet valid:\n" );
+		DBG_HDA ( 0, certificate->data, certificate->len );
+		return -EINVAL;
+	}
+
+	/* Skip over the first timestamp's data type */
+	asn1_skip ( &validity, format );
+
+	/* Normalize the upper bound and test it against the given stamp */
+	rc = x509_get_general_time ( &validity, valid_date, &format );
+	if ( rc != 0 ) {
+		DBG ( "Cannot locate notAfter timestamp:\n" );
+		DBG_HDA ( 0, certificate->data, certificate->len );
+		return rc;
+	}
+	if ( strcmp ( general_time, valid_date ) > 0 ) {
+		DBG ( "The certificate has expired:\n" );
+		DBG_HDA ( 0, certificate->data, certificate->len );
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/**
+ * Calculate a certificate's digest
+ *
+ * @v tbs		Cursor at the sequence byte of the tbsCertificate
+ * @v digest		Which algorithm to use
+ * @v digest_out	Allocated pointer to be filled with the result
+ */
+static void x509_cert_digest ( const struct asn1_cursor *tbs,
+			       struct digest_algorithm *digest,
+			       uint8_t *digest_out ) {
+	uint8_t digest_ctx[digest->ctxsize];
+	uint8_t buf[128];
+	size_t frag_len;
+	size_t len;
+	int offset = -2;
+
+	/* Seek back to the type byte for lengths greater than one byte */
+	while ( ((uint8_t *)tbs->data)[offset] != ASN1_SEQUENCE )
+		offset--;
+
+	/* Get a digest of the entire tbsCertificate structure */
+	digest_init ( digest, digest_ctx );
+	for ( len = tbs->len - offset; len; len -= frag_len ) {
+		frag_len = len;
+		if ( frag_len > sizeof ( buf ) )
+			frag_len = sizeof ( buf );
+		memcpy ( buf, tbs->data + offset, frag_len );
+		digest_update ( digest, digest_ctx, buf, frag_len );
+		offset += frag_len;
+	}
+	digest_final ( digest, digest_ctx, digest_out );
+}
+
+/**
+ * Retrieve the plaintext digest information from the encrypted signature
+ *
+ * @v signature		Pointer to an encrypted block
+ * @v issuer_key	The RSA public key used to decrypt the signature
+ * @v digest_info	ASN.1 cursor to be filled with the decrypted data
+ * @ret rc		Return status code
+ *
+ * Memory is allocated for digest_info->data, so remember to free it.
+ */
+static int x509_decrypt_signature ( uint8_t *signature,
+				    struct x509_rsa_public_key *issuer_key,
+				    struct asn1_cursor *digest_info ) {
+	RSA_CTX *rsa;
+	uint8_t rsa_out[issuer_key->modulus_len];
+	int rc;
+
+	/* Piggy-back on the included bigint library */
+	RSA_pub_key_new ( &rsa,
+			  issuer_key->modulus, issuer_key->modulus_len,
+			  issuer_key->exponent, issuer_key->exponent_len );
+	rc = RSA_decrypt ( rsa, signature, rsa_out, 0 );
+	RSA_free ( rsa );
+	if ( rc < 0 ) {
+		DBG ( "Signature decryption failed\n" );
+		return rc;
+	}
+
+	/* See RFC2313 section 10.1 for internals of the signature */
+	digest_info->data = malloc ( rc );
+	digest_info->len = rc;
+	memcpy ( digest_info->data, rsa_out, rc );
+	rc = ( asn1_enter ( digest_info, ASN1_SEQUENCE ) /* DigestInfo */ );
+	if ( rc != 0 ) {
+		DBG ( "Decrypted signature did not produce DigestInfo\n" );
+		return rc;
+	}
+
+	return 0;
+}
+
+/**
+ * Extract the public key from the embedded issuer certificate
+ *
+ * @v certificate	Certificate
+ * @v self_subject	The previously located certificate subject
+ * @v issuer_key	The RSA public key of the CA cert to be filled in
+ * @ret rc		Return status code
+ *
+ * Memory is allocated for the key, so remember x509_free_rsa_public_key().
+ */
+static int x509_issuer_public_key ( const struct asn1_cursor *certificate,
+				    const struct asn1_cursor *self_subject,
+				    struct x509_rsa_public_key *issuer_key ) {
+	struct image *image;
+	struct asn1_cursor ca_crt;
+	struct asn1_cursor issuer;
+	struct asn1_cursor subject;
+	char *pem;
+	char *begin;
+	char *end;
+	char *base64;
+	char buf[1];
+	int rc;
+
+	/* Locate issuer */
+	memcpy ( &issuer, certificate, sizeof ( issuer ) );
+	rc = ( asn1_enter ( &issuer, ASN1_SEQUENCE ), /* Certificate */
+	       asn1_enter ( &issuer, ASN1_SEQUENCE ), /* tbsCertificate */
+	       asn1_skip ( &issuer, ASN1_EXPLICIT_TAG ), /* version */
+	       asn1_skip ( &issuer, ASN1_INTEGER ), /* serialNumber */
+	       asn1_skip ( &issuer, ASN1_SEQUENCE ), /* signature */
+	       asn1_enter ( &issuer, ASN1_SEQUENCE ) /* issuer */ );
+	if ( rc != 0 ) {
+		DBG ( "Could not locate issuer in:\n" );
+		DBG_HDA ( 0, certificate->data, certificate->len );
+		return rc;
+	}
+
+	/* FIXME: Put PEM parsing somewhere to only be executed once */
+	for_each_image ( image )
+		if ( strcmp ( "ca.pem", image->name ) == 0 )
+			break;
+	if ( image == NULL || strcmp ( "ca.pem", image->name ) != 0 ) {
+		DBG ( "No embedded PEM file of trusted CAs was found\n" );
+		return -ENOENT;
+	}
+
+	/* Ensure the PEM data blocks are uniform regardless of position */
+	pem = malloc ( image->len + 3 );
+	memcpy ( pem + 1, (void *)image->data, image->len );
+	pem[0] = pem[image->len + 1] = '\n';
+	pem[image->len + 2] = '\0';
+
+	/* Go through each certificate and compare its CN to our issuer */
+	for ( end = pem;; ) {
+
+		/* Identify the next CA certificate or quit on failure */
+		rc = -EMSGSIZE; /* This code here should mean no more certs */
+		begin = strstr ( end, "\n-----BEGIN CERTIFICATE-----\n" );
+		if ( begin == NULL )
+			break;
+		rc = -EILSEQ;
+		end = strstr ( begin += 29, "\n-----END CERTIFICATE-----\n" );
+		if ( end == NULL )
+			break;
+
+		/* Base64-decode the certificate, allocating cert.data */
+		base64 = malloc ( end - begin + 1 );
+		memcpy ( base64, begin, end - begin );
+		base64[end - begin] = '\0';
+		ca_crt.data = malloc ( base64_decoded_max_len ( base64 ) );
+		ca_crt.len = base64_decode ( base64, (uint8_t *)ca_crt.data );
+		free ( base64 );
+		if ( ca_crt.len <= 0 ) {
+			DBG ( "Cannot decode the CA certificate:\n" );
+			free ( ca_crt.data );
+			continue;
+		}
+
+		/* Locate subject within the decoded CA certificate */
+		memcpy ( &subject, &ca_crt, sizeof ( subject ) );
+		rc = ( asn1_enter ( &subject, ASN1_SEQUENCE ), /* Certificate*/
+		       asn1_enter ( &subject, ASN1_SEQUENCE ), /* tbsCert */
+		       asn1_skip ( &subject, ASN1_EXPLICIT_TAG ), /* version */
+		       asn1_skip ( &subject, ASN1_INTEGER ), /* serialNumber */
+		       asn1_skip ( &subject, ASN1_SEQUENCE ), /* signature */
+		       asn1_skip ( &subject, ASN1_SEQUENCE ), /* issuer */
+		       asn1_skip ( &subject, ASN1_SEQUENCE ), /* validity */
+		       asn1_enter ( &subject, ASN1_SEQUENCE ) /* subject */ );
+		if ( rc != 0 ) {
+			DBG ( "Cannot locate subject in CA public cert:\n" );
+			DBG_HDA ( 0, ca_crt.data, ca_crt.len );
+			free ( ca_crt.data );
+			continue;
+		}
+
+		/* Compare the CA subject with the certificate issuer */
+		/* FIXME: Should implement RFC5280's (complex) DN comparison */
+		if ( issuer.len != subject.len ||
+		     memcmp ( issuer.data, subject.data, issuer.len ) != 0 ) {
+			DBG ( "This CA certificate is not the issuer:\n" );
+			DBG_HDA ( 0, ca_crt.data, ca_crt.len );
+			free ( ca_crt.data );
+			continue;
+		}
+
+		/* Create the CA certificate's RSA key */
+		rc = x509_rsa_public_key ( &ca_crt, issuer_key );
+		free ( ca_crt.data );
+		break;
+	}
+
+	/* Clean up and exit, unless no errors or matching certs were found */
+	free ( pem );
+	if ( rc != -EMSGSIZE )
+		return rc;
+
+	/* Without user-intervention, throw an error on self-signature */
+	if ( issuer.len == self_subject->len &&
+	     memcmp ( issuer.data, self_subject->data, issuer.len ) == 0 ) {
+		DBG ( "This certificate is self-signed\n" );
+		issuer_key->modulus = NULL;
+		rc = fetchf_named_setting ( "x509.self", buf, sizeof ( buf ) );
+		return rc > 0 ? 0 : -EINVAL;
+	}
+
+	/* The CA public key was not embedded */
+	DBG ( "The issuer of the certificate is unknown:\n" );
+	DBG_HDA ( 0, certificate->data, certificate->len );
+	return -EINVAL;
+}
+
+/**
+ * Ensure the certificate's signature is authentic
+ *
+ * @v certificate	Certificate
+ * @v issuer_key	The RSA public key used to decrypt the signature
+ * @ret rc		Return status code
+ */
+static int x509_verify_signature ( const struct asn1_cursor *certificate,
+				   struct x509_rsa_public_key *issuer_key ) {
+	struct asn1_cursor digAlg;
+	struct asn1_cursor digVal;
+	struct asn1_cursor digest_info;
+	struct asn1_cursor sigAlg;
+	struct asn1_cursor sigVal;
+	struct asn1_cursor signature;
+	struct asn1_cursor tbs;
+	uint8_t digest[SHA1_DIGEST_SIZE]; /* Largest supported digest size */
+	int rc;
+
+	/* Locate tbsCertificate */
+	memcpy ( &tbs, certificate, sizeof ( tbs ) );
+	rc = ( asn1_enter ( &tbs, ASN1_SEQUENCE ), /* Certificate */
+	       asn1_enter ( &tbs, ASN1_SEQUENCE ) /* tbsCertificate */ );
+	if ( rc != 0 ) {
+		DBG ( "Cannot locate tbsCertificate in:\n" );
+		DBG_HDA ( 0, certificate->data, certificate->len );
+		return rc;
+	}
+
+	/* Locate signature */
+	memcpy ( &signature, &tbs, sizeof ( signature ) );
+	rc = ( asn1_skip ( &signature, ASN1_EXPLICIT_TAG ), /* version */
+	       asn1_skip ( &signature, ASN1_INTEGER ), /* serialNumber */
+	       asn1_enter ( &signature, ASN1_SEQUENCE ) /* signature */ );
+	if ( rc != 0 ) {
+		DBG ( "Cannot locate signature in:\n" );
+		DBG_HDA ( 0, certificate->data, certificate->len );
+		return rc;
+	}
+
+	/* Locate signatureAlgorithm */
+	memcpy ( &sigAlg, certificate, sizeof ( sigAlg ) );
+	rc = ( asn1_enter ( &sigAlg, ASN1_SEQUENCE ), /* Certificate */
+	       asn1_skip ( &sigAlg, ASN1_SEQUENCE ), /* tbsCertificate */
+	       asn1_enter ( &sigAlg, ASN1_SEQUENCE ) /* signatureAlgorithm */);
+	if ( rc != 0 ) {
+		DBG ( "Cannot locate signatureAlgorithm in:\n" );
+		DBG_HDA ( 0, certificate->data, certificate->len );
+		return rc;
+	}
+
+	/* Verify the tbsCertificate signature matches signatureAlgorithm */
+	if ( signature.len == sigAlg.len &&
+	     memcmp ( signature.data, sigAlg.data, signature.len ) != 0 ) {
+		DBG ( "signatureAlgorithm does not match signature in:\n" );
+		DBG_HDA ( 0, certificate->data, certificate->len );
+		return -EILSEQ;
+	}
+
+	/* Locate signatureAlgorithm OID */
+	rc = ( asn1_enter ( &sigAlg, ASN1_OID ) /* signatureAlgorithm OID */ );
+	if ( rc != 0 ) {
+		DBG ( "Cannot locate the signatureAlgorithm OID in:\n" );
+		DBG_HDA ( 0, certificate->data, certificate->len );
+		return rc;
+	}
+
+	/* Verify the algorithm is supported, and create a respective digest */
+	if ( ( sigAlg.len == sizeof ( oid_sha1_rsa_signature ) ) &&
+	     ( memcmp ( sigAlg.data, &oid_sha1_rsa_signature,
+			sizeof ( oid_sha1_rsa_signature ) ) == 0 ) ) {
+		x509_cert_digest ( &tbs, &sha1_algorithm, digest );
+
+	} else if ( ( sigAlg.len == sizeof ( oid_md5_rsa_signature ) ) &&
+		    ( memcmp ( sigAlg.data, &oid_md5_rsa_signature,
+			       sizeof ( oid_md5_rsa_signature ) ) == 0 ) ) {
+		x509_cert_digest ( &tbs, &md5_algorithm, digest );
+
+	} else {
+		DBG ( "Unsupported signatureAlgorithm in:\n" );
+		DBG_HDA ( 0, certificate->data, certificate->len );
+		return -ENOTSUP;
+	}
+
+	/* Locate signatureValue */
+	memcpy ( &sigVal, certificate, sizeof ( sigVal ) );
+	rc = ( asn1_enter ( &sigVal, ASN1_SEQUENCE ), /* Certificate */
+	       asn1_skip ( &sigVal, ASN1_SEQUENCE ), /* tbsCertificate */
+	       asn1_skip ( &sigVal, ASN1_SEQUENCE ), /* signatureAlgorithm */
+	       asn1_enter ( &sigVal, ASN1_BIT_STRING ) /* signatureValue */ );
+	if ( rc != 0 ) {
+		DBG ( "Cannot locate signatureValue in:\n" );
+		DBG_HDA ( 0, certificate->data, certificate->len );
+		return rc;
+	}
+
+	/* Retrieve the issuer's digest and algorithm */
+	/* FIXME: Why do we get a leading null byte which breaks things? */
+	rc = x509_decrypt_signature ( sigVal.data + 1, issuer_key,
+				      &digest_info );
+	if ( rc != 0 )
+		return rc;
+
+	/* Locate the signature's digestAlgorithm */
+	memcpy ( &digAlg, &digest_info, sizeof ( digAlg ) );
+	rc = ( asn1_enter ( &digAlg, ASN1_SEQUENCE ), /* digestAlgorithm */
+	       asn1_enter ( &digAlg, ASN1_OID ) /* digestAlgorithm OID */ );
+	if ( rc != 0 ) {
+		DBG ( "Cannot locate digestAlgorithm in:\n" );
+		DBG_HDA ( 0, digest_info.data, digest_info.len );
+		return rc;
+	}
+
+	/* Ensure the issuer agrees with the certificate's digest algorithm */
+	if ( /* If it's not a signature/digest pair of SHA-1... */
+	     ( memcmp ( sigAlg.data, &oid_sha1_rsa_signature,
+			sizeof ( oid_sha1_rsa_signature ) ) != 0 ||
+	       memcmp ( digAlg.data, &oid_sha1_digest,
+			sizeof ( oid_sha1_digest ) ) != 0 ) &&
+	     /* And it's not a pair of MD5... */
+	     ( memcmp ( sigAlg.data, &oid_md5_rsa_signature,
+			sizeof ( oid_md5_rsa_signature ) ) != 0 ||
+	       memcmp ( digAlg.data, &oid_md5_digest,
+			sizeof ( oid_md5_digest ) ) != 0 ) ) {
+		DBG ( "The digest algorithm found was not expected in:\n" );
+		DBG_HDA ( 0, digest_info.data, digest_info.len );
+		return -EILSEQ;
+	}
+
+	/* Locate the signature's digest */
+	memcpy ( &digVal, &digest_info, sizeof ( digAlg ) );
+	rc = ( asn1_skip ( &digVal, ASN1_SEQUENCE ), /* digestAlgorithm */
+	       asn1_enter ( &digVal, ASN1_OCTET_STRING ) /* digest */ );
+	if ( rc != 0 ) {
+		DBG ( "Cannot locate digest in:\n" );
+		DBG_HDA ( 0, digest_info.data, digest_info.len );
+		return rc;
+	}
+
+	/* Finally, perform the actual digest comparison */
+	if ( memcmp ( digest, digVal.data, digVal.len ) != 0 ) {
+		DBG ( "The certificate signature is not valid in:\n" );
+		DBG_HDA ( 0, certificate->data, certificate->len );
+		return -EILSEQ;
+	}
+
+	/* Clean up memory */
+	free ( digest_info.data );
+
+	return 0;
+}
+
+/**
  * Identify X.509 certificate RSA modulus and public exponent
  *
  * @v certificate	Certificate
- * @v rsa		RSA public key to fill in
+ * @v rsa_pubkey	RSA public key to fill in
  * @ret rc		Return status code
  *
  * The caller is responsible for eventually calling
@@ -119,7 +746,7 @@ int x509_rsa_public_key ( const struct a
 	if ( rc != 0 ) {
 		DBG ( "Cannot locate algorithm:\n" );
 		DBG_HDA ( 0, certificate->data, certificate->len );
-	return rc;
+		return rc;
 	}
 	if ( ( algorithm.len != sizeof ( oid_rsa_encryption ) ) ||
 	     ( memcmp ( algorithm.data, &oid_rsa_encryption,
@@ -181,3 +808,81 @@ int x509_rsa_public_key ( const struct a
 
 	return 0;
 }
+
+/**
+ * Execute x509_rsa_public_key(), but follow up with certificate verification
+ *
+ * @v certificate	Certificate
+ * @v rsa_pubkey	RSA public key to fill in
+ * @ret rc		Return status code
+ */
+int x509_rsa_public_key_validate ( const struct asn1_cursor *certificate,
+				   struct x509_rsa_public_key *rsa_pubkey ) {
+	struct x509_rsa_public_key issuer_key;
+	struct asn1_cursor subject;
+	char now[16];
+	int rc;
+
+	/* Extract the key from the certificate */
+	if ( ( rc = x509_rsa_public_key ( certificate, rsa_pubkey ) ) != 0 )
+		return rc;
+
+	/* Allow for a scriptable escape of all certificate validation */
+	if ( fetchf_named_setting ( "x509.trust", now, sizeof ( now ) ) > 0 ) {
+		DBG ( "Accepting an unvalidated certificate\n" );
+		return 0;
+	}
+
+	/* Locate subject */
+	memcpy ( &subject, certificate, sizeof ( subject ) );
+	rc = ( asn1_enter ( &subject, ASN1_SEQUENCE ), /* Certificate */
+	       asn1_enter ( &subject, ASN1_SEQUENCE ), /* tbsCertificate */
+	       asn1_skip ( &subject, ASN1_EXPLICIT_TAG ), /* version */
+	       asn1_skip ( &subject, ASN1_INTEGER ), /* serialNumber */
+	       asn1_skip ( &subject, ASN1_SEQUENCE ), /* signature */
+	       asn1_skip ( &subject, ASN1_SEQUENCE ), /* issuer */
+	       asn1_skip ( &subject, ASN1_SEQUENCE ), /* validity */
+	       asn1_enter ( &subject, ASN1_SEQUENCE ) /* subject */ );
+	if ( rc != 0 ) {
+		DBG ( "Cannot locate subject in:\n" );
+		DBG_HDA ( 0, certificate->data, certificate->len );
+		return rc;
+	}
+
+	/* Check the subject common name is the expected service name */
+	if ( ( rc = x509_verify_service ( &subject ) ) != 0 )
+		return rc;
+
+	/* Check the certificate's validity period if there is a time source */
+	if ( x509_current_general_time ( now ) == 0 )
+		if ( ( rc = x509_is_valid_time ( certificate, now ) ) != 0 )
+			return rc;
+
+	/* Parse the embedded (trusted) certs, and extract the issuer's key */
+	rc = x509_issuer_public_key ( certificate, &subject, &issuer_key );
+	if ( rc != 0 )
+		return rc;
+
+	/* A successfully returned nil modulus implies accepted self-signing */
+	if ( issuer_key.modulus == NULL ) {
+		issuer_key.modulus = malloc ( rsa_pubkey->modulus_len +
+					      rsa_pubkey->exponent_len );
+		if ( ! issuer_key.modulus )
+			return -ENOMEM;
+		memcpy ( issuer_key.modulus, rsa_pubkey->modulus,
+			 rsa_pubkey->modulus_len + rsa_pubkey->exponent_len );
+		issuer_key.modulus_len = rsa_pubkey->modulus_len;
+		issuer_key.exponent_len = rsa_pubkey->exponent_len;
+		issuer_key.exponent = issuer_key.modulus +
+				      issuer_key.modulus_len;
+		DBG ( "Accepting a self-signed certificate\n" );
+	}
+
+	/* Check that the signature is legitimate */
+	rc = x509_verify_signature ( certificate, &issuer_key );
+
+	/* Clean up memory */
+	x509_free_rsa_public_key ( &issuer_key );
+
+	return rc;
+}
diff -Npru src.orig/include/gpxe/asn1.h src/include/gpxe/asn1.h
--- src.orig/include/gpxe/asn1.h	2010-06-29 15:31:33.000000000 -0400
+++ src/include/gpxe/asn1.h	2010-07-09 19:51:21.000000000 -0400
@@ -14,7 +14,12 @@ FILE_LICENCE ( GPL2_OR_LATER );
 #define ASN1_OCTET_STRING 0x04
 #define ASN1_NULL 0x05
 #define ASN1_OID 0x06
+#define ASN1_UTF8_STRING 0x0C
+#define ASN1_PRINTABLE_STRING 0x13
+#define ASN1_UTC_TIME 0x17
+#define ASN1_GENERAL_TIME 0x18
 #define ASN1_SEQUENCE 0x30
+#define ASN1_SET 0x31
 #define ASN1_IP_ADDRESS 0x40
 #define ASN1_EXPLICIT_TAG 0xa0
 
diff -Npru src.orig/include/gpxe/x509.h src/include/gpxe/x509.h
--- src.orig/include/gpxe/x509.h	2010-06-29 15:31:33.000000000 -0400
+++ src/include/gpxe/x509.h	2010-07-09 19:51:21.000000000 -0400
@@ -38,4 +38,8 @@ x509_free_rsa_public_key ( struct x509_r
 extern int x509_rsa_public_key ( const struct asn1_cursor *certificate,
 				 struct x509_rsa_public_key *rsa_pubkey );
 
+extern int x509_rsa_public_key_validate
+( const struct asn1_cursor *certificate,
+  struct x509_rsa_public_key *rsa_pubkey );
+
 #endif /* _GPXE_X509_H */
diff -Npru src.orig/net/tcp/https.c src/net/tcp/https.c
--- src.orig/net/tcp/https.c	2010-06-29 15:31:33.000000000 -0400
+++ src/net/tcp/https.c	2010-07-09 19:51:21.000000000 -0400
@@ -30,6 +30,8 @@ FILE_LICENCE ( GPL2_OR_LATER );
 #include <gpxe/tls.h>
 #include <gpxe/http.h>
 #include <gpxe/features.h>
+#include <gpxe/settings.h>
+#include <gpxe/uri.h>
 
 FEATURE ( FEATURE_PROTOCOL, "HTTPS", DHCP_EB_FEATURE_HTTPS, 1 );
 
@@ -41,6 +43,16 @@ FEATURE ( FEATURE_PROTOCOL, "HTTPS", DHC
  * @ret rc		Return status code
  */
 static int https_open ( struct xfer_interface *xfer, struct uri *uri ) {
+	int rc;
+
+	/* Define a trusted certificate CN to the hostname of the URI */
+	rc = storef_named_setting ( "x509.host",
+				    uri_get_field ( uri, URI_HOST ) );
+	if ( rc != 0 ) {
+		DBG ( "Could not set \"x509.service\" to the host name\n" );
+		return rc;
+	}
+
 	return http_open_filter ( xfer, uri, HTTPS_PORT, add_tls );
 }
 
diff -Npru src.orig/net/tls.c src/net/tls.c
--- src.orig/net/tls.c	2010-06-29 15:31:33.000000000 -0400
+++ src/net/tls.c	2010-07-09 19:51:21.000000000 -0400
@@ -127,8 +127,7 @@ static void tls_close ( struct tls_sessi
  * @v len		Length of buffer
  */
 static void tls_generate_random ( void *data, size_t len ) {
-	/* FIXME: Some real random data source would be nice... */
-	memset ( data, 0x01, len );
+	get_random_bytes ( data, len );
 }
 
 /**
@@ -886,8 +885,8 @@ static int tls_new_certificate ( struct 
 		}
 
 		// HACK
-		if ( ( rc = x509_rsa_public_key ( &cursor,
-						  &tls->rsa ) ) != 0 ) {
+		rc = x509_rsa_public_key_validate ( &cursor, &tls->rsa );
+		if ( rc != 0 ) {
 			DBGC ( tls, "TLS %p cannot determine RSA public key: "
 			       "%s\n", tls, strerror ( rc ) );
 			return rc;
