# comply with FIPS 140-2 by using only approved crypto algorithms
# when OpenSSL is detected to be running in FIPS mode
#
# HG changeset patch
# Parent 4f4fbc44d9451f493762925b65ee46764ea595ef

diff --git a/openssh-6.6p1/Makefile.in b/openssh-6.6p1/Makefile.in
--- a/openssh-6.6p1/Makefile.in
+++ b/openssh-6.6p1/Makefile.in
@@ -71,17 +71,18 @@ LIBSSH_OBJS=authfd.o authfile.o bufaux.o
 	readpass.o rsa.o ttymodes.o xmalloc.o addrmatch.o \
 	atomicio.o key.o dispatch.o kex.o mac.o uidswap.o uuencode.o misc.o \
 	monitor_fdpass.o rijndael.o ssh-dss.o ssh-ecdsa.o ssh-rsa.o dh.o \
 	kexdh.o kexgex.o kexdhc.o kexgexc.o bufec.o kexecdh.o kexecdhc.o \
 	msg.o progressmeter.o dns.o entropy.o gss-genr.o umac.o umac128.o \
 	ssh-pkcs11.o krl.o smult_curve25519_ref.o \
 	kexc25519.o kexc25519c.o poly1305.o chacha.o cipher-chachapoly.o \
 	ssh-ed25519.o digest-openssl.o hmac.o \
-	sc25519.o ge25519.o fe25519.o ed25519.o verify.o hash.o blocks.o
+	sc25519.o ge25519.o fe25519.o ed25519.o verify.o hash.o blocks.o \
+	fips.o
 
 SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \
 	sshconnect.o sshconnect1.o sshconnect2.o mux.o \
 	roaming_common.o roaming_client.o
 
 SSHDOBJS=sshd.o auth-rhosts.o auth-passwd.o auth-rsa.o auth-rh-rsa.o \
 	audit.o audit-bsm.o audit-linux.o platform.o \
 	sshpty.o sshlogin.o servconf.o serverloop.o \
diff --git a/openssh-6.6p1/auth-rsa.c b/openssh-6.6p1/auth-rsa.c
--- a/openssh-6.6p1/auth-rsa.c
+++ b/openssh-6.6p1/auth-rsa.c
@@ -44,16 +44,18 @@
 #include "ssh-gss.h"
 #endif
 #include "monitor_wrap.h"
 #include "ssh.h"
 #include "misc.h"
 
 #include "digest.h"
 
+#include "fips.h"
+
 /* import */
 extern ServerOptions options;
 
 /*
  * Session identifier that is used to bind key exchange and authentication
  * responses to a particular session.
  */
 extern u_char session_id[16];
@@ -84,45 +86,52 @@ auth_rsa_generate_challenge(Key *key)
 	if (BN_mod(challenge, challenge, key->rsa->n, ctx) == 0)
 		fatal("auth_rsa_generate_challenge: BN_mod failed");
 	BN_CTX_free(ctx);
 
 	return challenge;
 }
 
 int
-auth_rsa_verify_response(Key *key, BIGNUM *challenge, u_char response[16])
+auth_rsa_verify_response(Key *key, BIGNUM *challenge,
+    u_char response[SSH_DIGEST_MAX_LENGTH])
 {
-	u_char buf[32], mdbuf[16];
+	u_char buf[2 * SSH_DIGEST_MAX_LENGTH], mdbuf[SSH_DIGEST_MAX_LENGTH];
 	struct ssh_digest_ctx *md;
 	int len;
+	int dgst;
+	size_t dgst_len;
 
 	/* don't allow short keys */
 	if (BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) {
 		error("%s: RSA modulus too small: %d < minimum %d bits",
 		    __func__,
 		    BN_num_bits(key->rsa->n), SSH_RSA_MINIMUM_MODULUS_SIZE);
 		return (0);
 	}
 
-	/* The response is MD5 of decrypted challenge plus session id. */
+	dgst = fips_correct_dgst(SSH_DIGEST_MD5);
+	dgst_len = ssh_digest_bytes(dgst);
+
+	/* The response is a hash of decrypted challenge plus session id.
+	 * Normally this is MD5, in FIPS mode a stronger function is used. */
 	len = BN_num_bytes(challenge);
-	if (len <= 0 || len > 32)
+	if (len <= 0 || (unsigned int)len > (2 * dgst_len))
 		fatal("%s: bad challenge length %d", __func__, len);
-	memset(buf, 0, 32);
-	BN_bn2bin(challenge, buf + 32 - len);
-	if ((md = ssh_digest_start(SSH_DIGEST_MD5)) == NULL ||
-	    ssh_digest_update(md, buf, 32) < 0 ||
-	    ssh_digest_update(md, session_id, 16) < 0 ||
+	memset(buf, 0, sizeof(buf));
+	BN_bn2bin(challenge, buf + 2 * dgst_len - len);
+	if ((md = ssh_digest_start(dgst)) == NULL ||
+	    ssh_digest_update(md, buf, 2 * dgst_len) < 0 ||
+	    ssh_digest_update(md, session_id, dgst_len) < 0 ||
 	    ssh_digest_final(md, mdbuf, sizeof(mdbuf)) < 0)
 		fatal("%s: md5 failed", __func__);
 	ssh_digest_free(md);
 
 	/* Verify that the response is the original challenge. */
-	if (timingsafe_bcmp(response, mdbuf, 16) != 0) {
+	if (timingsafe_bcmp(response, mdbuf, dgst_len) != 0) {
 		/* Wrong answer. */
 		return (0);
 	}
 	/* Correct answer. */
 	return (1);
 }
 
 /*
@@ -130,17 +139,17 @@ auth_rsa_verify_response(Key *key, BIGNU
  * and returns true (non-zero) if the client gave the correct answer to
  * our challenge; returns zero if the client gives a wrong answer.
  */
 
 int
 auth_rsa_challenge_dialog(Key *key)
 {
 	BIGNUM *challenge, *encrypted_challenge;
-	u_char response[16];
+	u_char response[SSH_DIGEST_MAX_LENGTH];
 	int i, success;
 
 	if ((encrypted_challenge = BN_new()) == NULL)
 		fatal("auth_rsa_challenge_dialog: BN_new() failed");
 
 	challenge = PRIVSEP(auth_rsa_generate_challenge(key));
 
 	/* Encrypt the challenge with the public key. */
@@ -150,17 +159,17 @@ auth_rsa_challenge_dialog(Key *key)
 	packet_start(SSH_SMSG_AUTH_RSA_CHALLENGE);
 	packet_put_bignum(encrypted_challenge);
 	packet_send();
 	BN_clear_free(encrypted_challenge);
 	packet_write_wait();
 
 	/* Wait for a response. */
 	packet_read_expect(SSH_CMSG_AUTH_RSA_RESPONSE);
-	for (i = 0; i < 16; i++)
+	for (i = 0; i < ssh_digest_bytes(fips_dgst_min()); i++)
 		response[i] = (u_char)packet_get_char();
 	packet_check_eom();
 
 	success = PRIVSEP(auth_rsa_verify_response(key, challenge, response));
 	BN_clear_free(challenge);
 	return (success);
 }
 
diff --git a/openssh-6.6p1/cipher-ctr.c b/openssh-6.6p1/cipher-ctr.c
--- a/openssh-6.6p1/cipher-ctr.c
+++ b/openssh-6.6p1/cipher-ctr.c
@@ -22,16 +22,18 @@
 #include <stdarg.h>
 #include <string.h>
 
 #include <openssl/evp.h>
 
 #include "xmalloc.h"
 #include "log.h"
 
+#include "fips.h"
+
 /* compatibility with old or broken OpenSSL versions */
 #include "openbsd-compat/openssl-compat.h"
 
 #ifndef USE_BUILTIN_RIJNDAEL
 #include <openssl/aes.h>
 #endif
 
 struct ssh_aes_ctr_ctx
@@ -134,13 +136,15 @@ evp_aes_128_ctr(void)
 	aes_ctr.iv_len = AES_BLOCK_SIZE;
 	aes_ctr.key_len = 16;
 	aes_ctr.init = ssh_aes_ctr_init;
 	aes_ctr.cleanup = ssh_aes_ctr_cleanup;
 	aes_ctr.do_cipher = ssh_aes_ctr;
 #ifndef SSH_OLD_EVP
 	aes_ctr.flags = EVP_CIPH_CBC_MODE | EVP_CIPH_VARIABLE_LENGTH |
 	    EVP_CIPH_ALWAYS_CALL_INIT | EVP_CIPH_CUSTOM_IV;
+	if (fips_mode())
+		aes_ctr.flags |= EVP_CIPH_FLAG_FIPS;
 #endif
 	return (&aes_ctr);
 }
 
 #endif /* OPENSSL_HAVE_EVPCTR */
diff --git a/openssh-6.6p1/cipher.c b/openssh-6.6p1/cipher.c
--- a/openssh-6.6p1/cipher.c
+++ b/openssh-6.6p1/cipher.c
@@ -45,16 +45,18 @@
 
 #include "xmalloc.h"
 #include "log.h"
 #include "misc.h"
 #include "cipher.h"
 #include "buffer.h"
 #include "digest.h"
 
+#include "fips.h"
+
 /* compatibility with old or broken OpenSSL versions */
 #include "openbsd-compat/openssl-compat.h"
 
 extern const EVP_CIPHER *evp_ssh1_bf(void);
 extern const EVP_CIPHER *evp_ssh1_3des(void);
 extern void ssh1_3des_iv(EVP_CIPHER_CTX *, int, u_char *, int);
 
 struct Cipher {
@@ -66,17 +68,17 @@ struct Cipher {
 	u_int	auth_len;
 	u_int	discard_len;
 	u_int	flags;
 #define CFLAG_CBC		(1<<0)
 #define CFLAG_CHACHAPOLY	(1<<1)
 	const EVP_CIPHER	*(*evptype)(void);
 };
 
-static const struct Cipher ciphers[] = {
+static const struct Cipher ciphers_all[] = {
 	{ "none",	SSH_CIPHER_NONE, 8, 0, 0, 0, 0, 0, EVP_enc_null },
 	{ "des",	SSH_CIPHER_DES, 8, 8, 0, 0, 0, 1, EVP_des_cbc },
 	{ "3des",	SSH_CIPHER_3DES, 8, 16, 0, 0, 0, 1, evp_ssh1_3des },
 	{ "blowfish",	SSH_CIPHER_BLOWFISH, 8, 32, 0, 0, 0, 1, evp_ssh1_bf },
 
 	{ "3des-cbc",	SSH_CIPHER_SSH2, 8, 24, 0, 0, 0, 1, EVP_des_ede3_cbc },
 	{ "blowfish-cbc",
 			SSH_CIPHER_SSH2, 8, 16, 0, 0, 0, 1, EVP_bf_cbc },
@@ -99,27 +101,67 @@ static const struct Cipher ciphers[] = {
 	{ "aes256-gcm@openssh.com",
 			SSH_CIPHER_SSH2, 16, 32, 12, 16, 0, 0, EVP_aes_256_gcm },
 #endif
 	{ "chacha20-poly1305@openssh.com",
 			SSH_CIPHER_SSH2, 8, 64, 0, 16, 0, CFLAG_CHACHAPOLY, NULL },
 	{ NULL,		SSH_CIPHER_INVALID, 0, 0, 0, 0, 0, 0, NULL }
 };
 
+static const struct Cipher ciphers_fips140_2[] = {
+	{ "none",	SSH_CIPHER_NONE, 8, 0, 0, 0, 0, 0, EVP_enc_null },
+	{ "3des",	SSH_CIPHER_3DES, 8, 16, 0, 0, 0, 1, evp_ssh1_3des },
+
+	{ "3des-cbc",	SSH_CIPHER_SSH2, 8, 24, 0, 0, 0, 1, EVP_des_ede3_cbc },
+	{ "aes128-cbc",	SSH_CIPHER_SSH2, 16, 16, 0, 0, 0, 1, EVP_aes_128_cbc },
+	{ "aes192-cbc",	SSH_CIPHER_SSH2, 16, 24, 0, 0, 0, 1, EVP_aes_192_cbc },
+	{ "aes256-cbc",	SSH_CIPHER_SSH2, 16, 32, 0, 0, 0, 1, EVP_aes_256_cbc },
+	{ "rijndael-cbc@lysator.liu.se",
+			SSH_CIPHER_SSH2, 16, 32, 0, 0, 0, 1, EVP_aes_256_cbc },
+	{ "aes128-ctr",	SSH_CIPHER_SSH2, 16, 16, 0, 0, 0, 0, EVP_aes_128_ctr },
+	{ "aes192-ctr",	SSH_CIPHER_SSH2, 16, 24, 0, 0, 0, 0, EVP_aes_192_ctr },
+	{ "aes256-ctr",	SSH_CIPHER_SSH2, 16, 32, 0, 0, 0, 0, EVP_aes_256_ctr },
+#ifdef OPENSSL_HAVE_EVPGCM
+	{ "aes128-gcm@openssh.com",
+			SSH_CIPHER_SSH2, 16, 16, 12, 16, 0, 0, EVP_aes_128_gcm },
+	{ "aes256-gcm@openssh.com",
+			SSH_CIPHER_SSH2, 16, 32, 12, 16, 0, 0, EVP_aes_256_gcm },
+#endif
+	{ NULL,		SSH_CIPHER_INVALID, 0, 0, 0, 0, 0, 0, NULL }
+};
+
 /*--*/
 
+/* Returns array of ciphers available depending on selected FIPS mode */
+static const struct Cipher *
+fips_select_ciphers(void)
+{
+	int fips = fips_mode();
+	switch (fips) {
+		case 0:
+			return ciphers_all;
+		case 1:
+			return ciphers_fips140_2;
+		default:
+			/* should not be reached */
+			fatal("Fatal error: incorrect FIPS mode '%i' at %s:%u",
+			    fips, __FILE__, __LINE__);
+			return NULL;
+	}
+}
+
 /* Returns a list of supported ciphers separated by the specified char. */
 char *
 cipher_alg_list(char sep, int auth_only)
 {
 	char *ret = NULL;
 	size_t nlen, rlen = 0;
 	const Cipher *c;
 
-	for (c = ciphers; c->name != NULL; c++) {
+	for (c = fips_select_ciphers(); c->name != NULL; c++) {
 		if (c->number != SSH_CIPHER_SSH2)
 			continue;
 		if (auth_only && c->auth_len == 0)
 			continue;
 		if (ret != NULL)
 			ret[rlen++] = sep;
 		nlen = strlen(c->name);
 		ret = xrealloc(ret, 1, rlen + nlen + 2);
@@ -189,27 +231,27 @@ cipher_mask_ssh1(int client)
 	}
 	return mask;
 }
 
 const Cipher *
 cipher_by_name(const char *name)
 {
 	const Cipher *c;
-	for (c = ciphers; c->name != NULL; c++)
+	for (c = fips_select_ciphers(); c->name != NULL; c++)
 		if (strcmp(c->name, name) == 0)
 			return c;
 	return NULL;
 }
 
 const Cipher *
 cipher_by_number(int id)
 {
 	const Cipher *c;
-	for (c = ciphers; c->name != NULL; c++)
+	for (c = fips_select_ciphers(); c->name != NULL; c++)
 		if (c->number == id)
 			return c;
 	return NULL;
 }
 
 #define	CIPHER_SEP	","
 int
 ciphers_valid(const char *names)
@@ -241,17 +283,17 @@ ciphers_valid(const char *names)
  */
 
 int
 cipher_number(const char *name)
 {
 	const Cipher *c;
 	if (name == NULL)
 		return -1;
-	for (c = ciphers; c->name != NULL; c++)
+	for (c = fips_select_ciphers(); c->name != NULL; c++)
 		if (strcasecmp(c->name, name) == 0)
 			return c->number;
 	return -1;
 }
 
 char *
 cipher_name(int id)
 {
@@ -429,23 +471,24 @@ cipher_cleanup(CipherContext *cc)
  * Selects the cipher, and keys if by computing the MD5 checksum of the
  * passphrase and using the resulting 16 bytes as the key.
  */
 
 void
 cipher_set_key_string(CipherContext *cc, const Cipher *cipher,
     const char *passphrase, int do_encrypt)
 {
-	u_char digest[16];
+	u_char digest[SSH_DIGEST_MAX_LENGTH];
+	int dgst = fips_correct_dgst(SSH_DIGEST_MD5);
 
-	if (ssh_digest_memory(SSH_DIGEST_MD5, passphrase, strlen(passphrase),
+	if (ssh_digest_memory(dgst, passphrase, strlen(passphrase),
 	    digest, sizeof(digest)) < 0)
 		fatal("%s: md5 failed", __func__);
 
-	cipher_init(cc, cipher, digest, 16, NULL, 0, do_encrypt);
+	cipher_init(cc, cipher, digest, ssh_digest_bytes(dgst), NULL, 0, do_encrypt);
 
 	explicit_bzero(digest, sizeof(digest));
 }
 
 /*
  * Exports an IV from the CipherContext required to export the key
  * state back from the unprivileged child to the privileged parent
  * process.
diff --git a/openssh-6.6p1/dh.h b/openssh-6.6p1/dh.h
--- a/openssh-6.6p1/dh.h
+++ b/openssh-6.6p1/dh.h
@@ -40,16 +40,17 @@ DH	*dh_new_group14(void);
 
 void	 dh_gen_key(DH *, int);
 int	 dh_pub_is_valid(DH *, BIGNUM *);
 
 int	 dh_estimate(int);
 
 /* Min and max values from RFC4419. */
 #define DH_GRP_MIN	1024
+#define DH_GRP_MIN_FIPS	2048
 #define DH_GRP_MAX	8192
 
 /*
  * Values for "type" field of moduli(5)
  * Specifies the internal structure of the prime modulus.
  */
 #define MODULI_TYPE_UNKNOWN		(0)
 #define MODULI_TYPE_UNSTRUCTURED	(1)
diff --git a/openssh-6.6p1/fips.c b/openssh-6.6p1/fips.c
new file mode 100644
--- /dev/null
+++ b/openssh-6.6p1/fips.c
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 2012 Petr Cerny.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "includes.h"
+
+#include "fips.h"
+
+#include "dh.h"
+#include "digest.h"
+#include "key.h"
+#include "log.h"
+
+#include <openssl/crypto.h>
+
+static int fips_state = -1;
+
+static int
+fips_check_required_env(void)
+{
+	int fips_required = 0;
+	char *env = getenv(SSH_FORCE_FIPS_ENV);
+
+	if (env) {
+		errno = 0;
+		fips_required = strtol(env, NULL, 10);
+		if (errno) {
+			debug("bogus value in the %s environment variable, ignoring\n"
+			    , SSH_FORCE_FIPS_ENV);
+			fips_required = 0;
+		} else
+			fips_required = 1;
+	}
+	return fips_required;
+}
+
+int
+fips_mode(void)
+{
+	if (-1 == fips_state) {
+		fips_state = FIPS_mode();
+		if (fips_state)
+			debug("FIPS mode initialized");
+		else {
+			if (fips_check_required_env()) {
+				debug("FIPS mode requested through the environment variable '%s'"
+				    , SSH_FORCE_FIPS_ENV);
+				if (!FIPS_mode_set(1))
+					fatal("Unable to enter FIPS mode as requested through the environment variable '%s'"
+					    , SSH_FORCE_FIPS_ENV);
+				fips_state = 1;
+			}
+		}
+	}
+	return fips_state;
+}
+
+enum fp_type
+fips_correct_fp_type(enum fp_type fp)
+{
+	int fips;
+	enum fp_type fp_fix = fp;
+
+	fips = fips_mode();
+	switch (fips) {
+		case 0:
+			break;
+		case 1:
+			if (SSH_FP_MD5 == fp) {
+				fp_fix = SSH_FP_SHA1;
+				debug("MD5 not allowed in FIPS 140-2 mode, "
+				    "using SHA-1 for key fingerprints instead.");
+			}
+			break;
+		default:
+			/* should not be reached */
+			fatal("Fatal error: incorrect FIPS mode '%i' at %s:%u",
+			    fips, __FILE__, __LINE__);
+	}
+
+	return fp_fix;
+}
+
+int
+fips_correct_dgst(int digest)
+{
+	int fips;
+	int rv = -1;
+
+	fips = fips_mode();
+	switch (fips) {
+		case 0:
+			rv = digest;
+			break;
+		case 1:
+			switch (digest) {
+				case SSH_DIGEST_MD5:
+				case SSH_DIGEST_RIPEMD160:
+					debug("MD5/RIPEMD160 digests not allowed in FIPS 140-2 mode"
+					    "using SHA-1 instead.");
+					rv = SSH_DIGEST_SHA1;
+					break;
+				default:
+					rv = digest;
+					break;
+			}
+			break;
+		default:
+			/* should not be reached */
+			fatal("Fatal error: incorrect FIPS mode '%i' at %s:%u",
+			    fips, __FILE__, __LINE__);
+	}
+
+	return rv;
+}
+
+int
+fips_dgst_min(void)
+{
+	int fips;
+	int dgst;
+
+	fips = fips_mode();
+	switch (fips) {
+		case 0:
+			dgst = SSH_DIGEST_MD5;
+			break;
+		case 1:
+			dgst = SSH_DIGEST_SHA1;
+			break;
+		default:
+			/* should not be reached */
+			fatal("Fatal error: incorrect FIPS mode '%i' at %s:%u",
+			    fips, __FILE__, __LINE__);
+	}
+	return dgst;
+}
+
+int
+fips_dh_grp_min(void)
+{
+	int fips;
+	int dh;
+
+	fips = fips_mode();
+	switch (fips) {
+		case 0:
+			dh = DH_GRP_MIN;
+			break;
+		case 1:
+			dh = DH_GRP_MIN_FIPS;
+			break;
+		default:
+			/* should not be reached */
+			fatal("Fatal error: incorrect FIPS mode '%i' at %s:%u",
+			    fips, __FILE__, __LINE__);
+	}
+	return dh;
+}
+
diff --git a/openssh-6.6p1/fips.h b/openssh-6.6p1/fips.h
new file mode 100644
--- /dev/null
+++ b/openssh-6.6p1/fips.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2012 Petr Cerny.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef FIPS_H
+#define FIPS_H
+
+#include "key.h"
+
+#define SSH_FORCE_FIPS_ENV	"SSH_FORCE_FIPS"
+
+int	 fips_mode(void);
+int	 fips_correct_dgst(int);
+int	 fips_dgst_min(void);
+int	 fips_dh_grp_min(void);
+enum fp_type	 fips_correct_fp_type(enum fp_type);
+
+#endif
+
diff --git a/openssh-6.6p1/hmac.c b/openssh-6.6p1/hmac.c
--- a/openssh-6.6p1/hmac.c
+++ b/openssh-6.6p1/hmac.c
@@ -139,17 +139,17 @@ ssh_hmac_free(struct ssh_hmac_ctx *ctx)
 /* cc -DTEST hmac.c digest.c buffer.c cleanup.c fatal.c log.c xmalloc.c -lcrypto */
 static void
 hmac_test(void *key, size_t klen, void *m, size_t mlen, u_char *e, size_t elen)
 {
 	struct ssh_hmac_ctx	*ctx;
 	size_t			 i;
 	u_char			 digest[16];
 
-	if ((ctx = ssh_hmac_start(SSH_DIGEST_MD5)) == NULL)
+	if ((ctx = ssh_hmac_start(fips_correct_dgst(SSH_DIGEST_MD5))) == NULL)
 		printf("ssh_hmac_start failed");
 	if (ssh_hmac_init(ctx, key, klen) < 0 ||
 	    ssh_hmac_update(ctx, m, mlen) < 0 ||
 	    ssh_hmac_final(ctx, digest, sizeof(digest)) < 0)
 		printf("ssh_hmac_xxx failed");
 	ssh_hmac_free(ctx);
 
 	if (memcmp(e, digest, elen)) {
diff --git a/openssh-6.6p1/kex.c b/openssh-6.6p1/kex.c
--- a/openssh-6.6p1/kex.c
+++ b/openssh-6.6p1/kex.c
@@ -46,16 +46,18 @@
 #include "log.h"
 #include "mac.h"
 #include "match.h"
 #include "dispatch.h"
 #include "monitor.h"
 #include "roaming.h"
 #include "digest.h"
 
+#include "fips.h"
+
 #if OPENSSL_VERSION_NUMBER >= 0x00907000L
 # if defined(HAVE_EVP_SHA256)
 # define evp_ssh_sha256 EVP_sha256
 # else
 extern const EVP_MD *evp_ssh_sha256(void);
 # endif
 #endif
 
@@ -64,17 +66,17 @@ static void kex_kexinit_finish(Kex *);
 static void kex_choose_conf(Kex *);
 
 struct kexalg {
 	char *name;
 	int type;
 	int ec_nid;
 	int hash_alg;
 };
-static const struct kexalg kexalgs[] = {
+static const struct kexalg kexalgs_all[] = {
 	{ KEX_DH1, KEX_DH_GRP1_SHA1, 0, SSH_DIGEST_SHA1 },
 	{ KEX_DH14, KEX_DH_GRP14_SHA1, 0, SSH_DIGEST_SHA1 },
 	{ KEX_DHGEX_SHA1, KEX_DH_GEX_SHA1, 0, SSH_DIGEST_SHA1 },
 #ifdef HAVE_EVP_SHA256
 	{ KEX_DHGEX_SHA256, KEX_DH_GEX_SHA256, 0, SSH_DIGEST_SHA256 },
 #endif
 #ifdef OPENSSL_HAS_ECC
 	{ KEX_ECDH_SHA2_NISTP256, KEX_ECDH_SHA2,
@@ -88,40 +90,76 @@ static const struct kexalg kexalgs[] = {
 #endif
 	{ KEX_DH1, KEX_DH_GRP1_SHA1, 0, SSH_DIGEST_SHA1 },
 #ifdef HAVE_EVP_SHA256
 	{ KEX_CURVE25519_SHA256, KEX_C25519_SHA256, 0, SSH_DIGEST_SHA256 },
 #endif
 	{ NULL, -1, -1, -1},
 };
 
+static const struct kexalg kexalgs_fips140_2[] = {
+	{ KEX_DH14, KEX_DH_GRP14_SHA1, 0, SSH_DIGEST_SHA1 },
+	{ KEX_DHGEX_SHA1, KEX_DH_GEX_SHA1, 0, SSH_DIGEST_SHA1 },
+#ifdef HAVE_EVP_SHA256
+	{ KEX_DHGEX_SHA256, KEX_DH_GEX_SHA256, 0, SSH_DIGEST_SHA256 },
+#endif
+#ifdef OPENSSL_HAS_ECC
+	{ KEX_ECDH_SHA2_NISTP256, KEX_ECDH_SHA2,
+	    NID_X9_62_prime256v1, SSH_DIGEST_SHA256 },
+	{ KEX_ECDH_SHA2_NISTP384, KEX_ECDH_SHA2, NID_secp384r1,
+	    SSH_DIGEST_SHA384 },
+# ifdef OPENSSL_HAS_NISTP521
+	{ KEX_ECDH_SHA2_NISTP521, KEX_ECDH_SHA2, NID_secp521r1,
+	    SSH_DIGEST_SHA512 },
+# endif
+#endif
+	{ NULL, -1, -1, -1},
+};
+
+/* Returns array of macs available depending on selected FIPS mode */
+static const struct kexalg *
+fips_select_kexalgs(void)
+{
+	int fips = fips_mode();
+	switch (fips) {
+		case 0:
+			return kexalgs_all;
+		case 1:
+			return kexalgs_fips140_2;
+		default:
+			/* should not be reached */
+			fatal("Fatal error: incorrect FIPS mode '%i' at %s:%u",
+			    fips, __FILE__, __LINE__);
+	}
+}
+
 char *
 kex_alg_list(char sep)
 {
 	char *ret = NULL;
 	size_t nlen, rlen = 0;
 	const struct kexalg *k;
 
-	for (k = kexalgs; k->name != NULL; k++) {
+	for (k = fips_select_kexalgs(); k->name != NULL; k++) {
 		if (ret != NULL)
 			ret[rlen++] = sep;
 		nlen = strlen(k->name);
 		ret = xrealloc(ret, 1, rlen + nlen + 2);
 		memcpy(ret + rlen, k->name, nlen + 1);
 		rlen += nlen;
 	}
 	return ret;
 }
 
 static const struct kexalg *
 kex_alg_by_name(const char *name)
 {
 	const struct kexalg *k;
 
-	for (k = kexalgs; k->name != NULL; k++) {
+	for (k = fips_select_kexalgs(); k->name != NULL; k++) {
 		if (strcmp(k->name, name) == 0)
 			return k;
 	}
 	return NULL;
 }
 
 /* Validate KEX method name list */
 int
@@ -638,19 +676,21 @@ kex_get_newkeys(int mode)
 }
 
 void
 derive_ssh1_session_id(BIGNUM *host_modulus, BIGNUM *server_modulus,
     u_int8_t cookie[8], u_int8_t id[16])
 {
 	u_int8_t nbuf[2048], obuf[SSH_DIGEST_MAX_LENGTH];
 	int len;
+	int digest;
 	struct ssh_digest_ctx *hashctx;
 
-	if ((hashctx = ssh_digest_start(SSH_DIGEST_MD5)) == NULL)
+	digest = fips_correct_dgst(SSH_DIGEST_MD5);
+	if ((hashctx = ssh_digest_start(digest)) == NULL)
 		fatal("%s: ssh_digest_start", __func__);
 
 	len = BN_num_bytes(host_modulus);
 	if (len < (512 / 8) || (u_int)len > sizeof(nbuf))
 		fatal("%s: bad host modulus (len %d)", __func__, len);
 	BN_bn2bin(host_modulus, nbuf);
 	if (ssh_digest_update(hashctx, nbuf, len) != 0)
 		fatal("%s: ssh_digest_update failed", __func__);
@@ -659,17 +699,17 @@ derive_ssh1_session_id(BIGNUM *host_modu
 	if (len < (512 / 8) || (u_int)len > sizeof(nbuf))
 		fatal("%s: bad server modulus (len %d)", __func__, len);
 	BN_bn2bin(server_modulus, nbuf);
 	if (ssh_digest_update(hashctx, nbuf, len) != 0 ||
 	    ssh_digest_update(hashctx, cookie, 8) != 0)
 		fatal("%s: ssh_digest_update failed", __func__);
 	if (ssh_digest_final(hashctx, obuf, sizeof(obuf)) != 0)
 		fatal("%s: ssh_digest_final failed", __func__);
-	memcpy(id, obuf, ssh_digest_bytes(SSH_DIGEST_MD5));
+	memcpy(id, obuf, ssh_digest_bytes(digest));
 
 	explicit_bzero(nbuf, sizeof(nbuf));
 	explicit_bzero(obuf, sizeof(obuf));
 }
 
 #if defined(DEBUG_KEX) || defined(DEBUG_KEXDH) || defined(DEBUG_KEXECDH)
 void
 dump_digest(char *msg, u_char *digest, int len)
diff --git a/openssh-6.6p1/kexgexc.c b/openssh-6.6p1/kexgexc.c
--- a/openssh-6.6p1/kexgexc.c
+++ b/openssh-6.6p1/kexgexc.c
@@ -41,16 +41,18 @@
 #include "cipher.h"
 #include "kex.h"
 #include "log.h"
 #include "packet.h"
 #include "dh.h"
 #include "ssh2.h"
 #include "compat.h"
 
+#include "fips.h"
+
 void
 kexgex_client(Kex *kex)
 {
 	BIGNUM *dh_server_pub = NULL, *shared_secret = NULL;
 	BIGNUM *p = NULL, *g = NULL;
 	Key *server_host_key;
 	u_char *kbuf, *hash, *signature = NULL, *server_host_key_blob = NULL;
 	u_int klen, slen, sbloblen, hashlen;
@@ -59,23 +61,23 @@ kexgex_client(Kex *kex)
 	DH *dh;
 
 	nbits = dh_estimate(kex->dh_need * 8);
 
 	if (datafellows & SSH_OLD_DHGEX) {
 		/* Old GEX request */
 		packet_start(SSH2_MSG_KEX_DH_GEX_REQUEST_OLD);
 		packet_put_int(nbits);
-		min = DH_GRP_MIN;
+		min = fips_dh_grp_min();
 		max = DH_GRP_MAX;
 
 		debug("SSH2_MSG_KEX_DH_GEX_REQUEST_OLD(%u) sent", nbits);
 	} else {
 		/* New GEX request */
-		min = DH_GRP_MIN;
+		min = fips_dh_grp_min();
 		max = DH_GRP_MAX;
 		packet_start(SSH2_MSG_KEX_DH_GEX_REQUEST);
 		packet_put_int(min);
 		packet_put_int(nbits);
 		packet_put_int(max);
 
 		debug("SSH2_MSG_KEX_DH_GEX_REQUEST(%u<%u<%u) sent",
 		    min, nbits, max);
diff --git a/openssh-6.6p1/kexgexs.c b/openssh-6.6p1/kexgexs.c
--- a/openssh-6.6p1/kexgexs.c
+++ b/openssh-6.6p1/kexgexs.c
@@ -45,16 +45,18 @@
 #include "dh.h"
 #include "ssh2.h"
 #include "compat.h"
 #ifdef GSSAPI
 #include "ssh-gss.h"
 #endif
 #include "monitor_wrap.h"
 
+#include "fips.h"
+
 void
 kexgex_server(Kex *kex)
 {
 	BIGNUM *shared_secret = NULL, *dh_client_pub = NULL;
 	Key *server_host_public, *server_host_private;
 	DH *dh;
 	u_char *kbuf, *hash, *signature = NULL, *server_host_key_blob = NULL;
 	u_int sbloblen, klen, slen, hashlen;
@@ -71,26 +73,26 @@ kexgex_server(Kex *kex)
 
 	type = packet_read();
 	switch (type) {
 	case SSH2_MSG_KEX_DH_GEX_REQUEST:
 		debug("SSH2_MSG_KEX_DH_GEX_REQUEST received");
 		omin = min = packet_get_int();
 		onbits = nbits = packet_get_int();
 		omax = max = packet_get_int();
-		min = MAX(DH_GRP_MIN, min);
+		min = MAX(fips_dh_grp_min(), min);
 		max = MIN(DH_GRP_MAX, max);
-		nbits = MAX(DH_GRP_MIN, nbits);
+		nbits = MAX(fips_dh_grp_min(), nbits);
 		nbits = MIN(DH_GRP_MAX, nbits);
 		break;
 	case SSH2_MSG_KEX_DH_GEX_REQUEST_OLD:
 		debug("SSH2_MSG_KEX_DH_GEX_REQUEST_OLD received");
 		onbits = nbits = packet_get_int();
 		/* unused for old GEX */
-		omin = min = DH_GRP_MIN;
+		omin = min = fips_dh_grp_min();
 		omax = max = DH_GRP_MAX;
 		break;
 	default:
 		fatal("protocol error during kex, no DH_GEX_REQUEST: %d", type);
 	}
 	packet_check_eom();
 
 	if (omax < omin || onbits < omin || omax < onbits)
diff --git a/openssh-6.6p1/key.c b/openssh-6.6p1/key.c
--- a/openssh-6.6p1/key.c
+++ b/openssh-6.6p1/key.c
@@ -53,16 +53,18 @@
 #include "rsa.h"
 #include "uuencode.h"
 #include "buffer.h"
 #include "log.h"
 #include "misc.h"
 #include "ssh2.h"
 #include "digest.h"
 
+#include "fips.h"
+
 static int to_blob(const Key *, u_char **, u_int *, int);
 static Key *key_from_blob2(const u_char *, u_int, int);
 
 static struct KeyCert *
 cert_new(void)
 {
 	struct KeyCert *cert;
 
@@ -664,16 +666,19 @@ key_fp_type_select(void)
 				error("invalid key type in environment variable "
 				    SSH_FP_TYPE_ENVVAR ": '%s' - falling back to MD5.",
 				    env);
 				fp = SSH_FP_MD5;
 			}
 		} else
 			fp = SSH_FP_MD5;
 
+		if (fips_mode())
+			fp = fips_correct_fp_type(fp);
+
 		fp_defined = 1;
    }
    return fp;
 }
 
 /* 
  * string lengths must be less or equal to SSH_FP_TYPE_STRLEN (defined in
  * key.h) as to fit into the fingerprint string buffer
diff --git a/openssh-6.6p1/mac.c b/openssh-6.6p1/mac.c
--- a/openssh-6.6p1/mac.c
+++ b/openssh-6.6p1/mac.c
@@ -39,33 +39,35 @@
 #include "kex.h"
 #include "mac.h"
 #include "misc.h"
 
 #include "digest.h"
 #include "hmac.h"
 #include "umac.h"
 
+#include "fips.h"
+
 #include "openbsd-compat/openssl-compat.h"
 
 #define SSH_DIGEST	1	/* SSH_DIGEST_XXX */
 #define SSH_UMAC	2	/* UMAC (not integrated with OpenSSL) */
 #define SSH_UMAC128	3
 
 struct macalg {
 	char		*name;
 	int		type;
 	int		alg;
 	int		truncatebits;	/* truncate digest if != 0 */
 	int		key_len;	/* just for UMAC */
 	int		len;		/* just for UMAC */
 	int		etm;		/* Encrypt-then-MAC */
 };
 
-static const struct macalg macs[] = {
+static const struct macalg macs_all[] = {
 	/* Encrypt-and-MAC (encrypt-and-authenticate) variants */
 	{ "hmac-sha1",				SSH_DIGEST, SSH_DIGEST_SHA1, 0, 0, 0, 0 },
 	{ "hmac-sha1-96",			SSH_DIGEST, SSH_DIGEST_SHA1, 96, 0, 0, 0 },
 #ifdef HAVE_EVP_SHA256
 	{ "hmac-sha2-256",			SSH_DIGEST, SSH_DIGEST_SHA256, 0, 0, 0, 0 },
 	{ "hmac-sha2-512",			SSH_DIGEST, SSH_DIGEST_SHA512, 0, 0, 0, 0 },
 #endif
 	{ "hmac-md5",				SSH_DIGEST, SSH_DIGEST_MD5, 0, 0, 0, 0 },
@@ -86,25 +88,59 @@ static const struct macalg macs[] = {
 	{ "hmac-md5-96-etm@openssh.com",	SSH_DIGEST, SSH_DIGEST_MD5, 96, 0, 0, 1 },
 	{ "hmac-ripemd160-etm@openssh.com",	SSH_DIGEST, SSH_DIGEST_RIPEMD160, 0, 0, 0, 1 },
 	{ "umac-64-etm@openssh.com",		SSH_UMAC, 0, 0, 128, 64, 1 },
 	{ "umac-128-etm@openssh.com",		SSH_UMAC128, 0, 0, 128, 128, 1 },
 
 	{ NULL,					0, 0, 0, 0, 0, 0 }
 };
 
+static const struct macalg macs_fips140_2[] = {
+	/* Encrypt-and-MAC (encrypt-and-authenticate) variants */
+	{ "hmac-sha1",				SSH_DIGEST, SSH_DIGEST_SHA1, 0, 0, 0, 0 },
+#ifdef HAVE_EVP_SHA256
+	{ "hmac-sha2-256",			SSH_DIGEST, SSH_DIGEST_SHA256, 0, 0, 0, 0 },
+	{ "hmac-sha2-512",			SSH_DIGEST, SSH_DIGEST_SHA512, 0, 0, 0, 0 },
+#endif
+
+	/* Encrypt-then-MAC variants */
+	{ "hmac-sha1-etm@openssh.com",		SSH_DIGEST, SSH_DIGEST_SHA1, 0, 0, 0, 1 },
+#ifdef HAVE_EVP_SHA256
+	{ "hmac-sha2-256-etm@openssh.com",	SSH_DIGEST, SSH_DIGEST_SHA256, 0, 0, 0, 1 },
+	{ "hmac-sha2-512-etm@openssh.com",	SSH_DIGEST, SSH_DIGEST_SHA512, 0, 0, 0, 1 },
+#endif
+	{ NULL,					0, 0, 0, 0, 0, 0 }
+};
+
+/* Returns array of macs available depending on selected FIPS mode */
+static const struct macalg *
+fips_select_macs(void)
+{
+	int fips = fips_mode();
+	switch (fips) {
+		case 0:
+			return macs_all;
+		case 1:
+			return macs_fips140_2;
+		default:
+			/* should not be reached */
+			fatal("Fatal error: incorrect FIPS mode '%i' at %s:%u",
+			    fips, __FILE__, __LINE__);
+	}
+}
+
 /* Returns a list of supported MACs separated by the specified char. */
 char *
 mac_alg_list(char sep)
 {
 	char *ret = NULL;
 	size_t nlen, rlen = 0;
 	const struct macalg *m;
 
-	for (m = macs; m->name != NULL; m++) {
+	for (m = fips_select_macs(); m->name != NULL; m++) {
 		if (ret != NULL)
 			ret[rlen++] = sep;
 		nlen = strlen(m->name);
 		ret = xrealloc(ret, 1, rlen + nlen + 2);
 		memcpy(ret + rlen, m->name, nlen + 1);
 		rlen += nlen;
 	}
 	return ret;
@@ -128,17 +164,17 @@ mac_setup_by_alg(Mac *mac, const struct 
 	mac->etm = macalg->etm;
 }
 
 int
 mac_setup(Mac *mac, char *name)
 {
 	const struct macalg *m;
 
-	for (m = macs; m->name != NULL; m++) {
+	for (m = fips_select_macs(); m->name != NULL; m++) {
 		if (strcmp(name, m->name) != 0)
 			continue;
 		if (mac != NULL) {
 			mac_setup_by_alg(mac, m);
 			debug2("mac_setup: setup %s", name);
 		}
 		return (0);
 	}
diff --git a/openssh-6.6p1/myproposal.h b/openssh-6.6p1/myproposal.h
--- a/openssh-6.6p1/myproposal.h
+++ b/openssh-6.6p1/myproposal.h
@@ -83,16 +83,22 @@
 # define KEX_DEFAULT_KEX \
 	KEX_CURVE25519_METHODS \
 	KEX_ECDH_METHODS \
 	KEX_SHA256_METHODS \
 	"diffie-hellman-group-exchange-sha1," \
 	"diffie-hellman-group14-sha1," \
 	"diffie-hellman-group1-sha1"
 
+# define KEX_DEFAULT_KEX_FIPS140_2 \
+	KEX_ECDH_METHODS \
+	KEX_SHA256_METHODS \
+	"diffie-hellman-group-exchange-sha1," \
+	"diffie-hellman-group14-sha1"
+
 #define	KEX_DEFAULT_PK_ALG	\
 	HOSTKEY_ECDSA_CERT_METHODS \
 	"ssh-ed25519-cert-v01@openssh.com," \
 	"ssh-rsa-cert-v01@openssh.com," \
 	"ssh-dss-cert-v01@openssh.com," \
 	"ssh-rsa-cert-v00@openssh.com," \
 	"ssh-dss-cert-v00@openssh.com," \
 	HOSTKEY_ECDSA_METHODS \
@@ -104,16 +110,21 @@
 
 #define	KEX_DEFAULT_ENCRYPT \
 	"aes128-ctr,aes192-ctr,aes256-ctr," \
 	"arcfour256,arcfour128," \
 	AESGCM_CIPHER_MODES \
 	"chacha20-poly1305@openssh.com," \
 	"aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc," \
 	"aes192-cbc,aes256-cbc,arcfour,rijndael-cbc@lysator.liu.se"
+#define	KEX_DEFAULT_ENCRYPT_FIPS140_2 \
+	"aes128-ctr,aes192-ctr,aes256-ctr," \
+	AESGCM_CIPHER_MODES \
+	"aes128-cbc,3des-cbc," \
+	"aes192-cbc,aes256-cbc,rijndael-cbc@lysator.liu.se"
 
 #define	KEX_DEFAULT_MAC \
 	"hmac-md5-etm@openssh.com," \
 	"hmac-sha1-etm@openssh.com," \
 	"umac-64-etm@openssh.com," \
 	"umac-128-etm@openssh.com," \
 	"hmac-sha2-256-etm@openssh.com," \
 	"hmac-sha2-512-etm@openssh.com," \
@@ -124,16 +135,22 @@
 	"hmac-sha1," \
 	"umac-64@openssh.com," \
 	"umac-128@openssh.com," \
 	SHA2_HMAC_MODES \
 	"hmac-ripemd160," \
 	"hmac-ripemd160@openssh.com," \
 	"hmac-sha1-96," \
 	"hmac-md5-96"
+#define	KEX_DEFAULT_MAC_FIPS140_2 \
+	"hmac-sha1-etm@openssh.com," \
+	"hmac-sha2-256-etm@openssh.com," \
+	"hmac-sha2-512-etm@openssh.com," \
+	"hmac-sha1," \
+	SHA2_HMAC_MODES \
 
 #define	KEX_DEFAULT_COMP	"none,zlib@openssh.com,zlib"
 #define	KEX_DEFAULT_LANG	""
 
 
 static char *myproposal[PROPOSAL_MAX] = {
 	KEX_DEFAULT_KEX,
 	KEX_DEFAULT_PK_ALG,
diff --git a/openssh-6.6p1/ssh-keygen.c b/openssh-6.6p1/ssh-keygen.c
--- a/openssh-6.6p1/ssh-keygen.c
+++ b/openssh-6.6p1/ssh-keygen.c
@@ -49,16 +49,18 @@
 #include "hostfile.h"
 #include "dns.h"
 #include "ssh.h"
 #include "ssh2.h"
 #include "ssh-pkcs11.h"
 #include "atomicio.h"
 #include "krl.h"
 
+#include "fips.h"
+
 /* Number of bits in the RSA/DSA key.  This value can be set on the command line. */
 #define DEFAULT_BITS		2048
 #define DEFAULT_BITS_DSA	1024
 #define DEFAULT_BITS_ECDSA	256
 u_int32_t bits = 0;
 
 /*
  * Flag indicating that we just want to change the passphrase.  This can be
@@ -884,38 +886,53 @@ do_fingerprint(struct passwd *pw)
 		exit(1);
 	}
 	exit(0);
 }
 
 static void
 do_gen_all_hostkeys(struct passwd *pw)
 {
-	struct {
+	struct Key_types {
 		char *key_type;
 		char *key_type_display;
 		char *path;
-	} key_types[] = {
+	};
+	struct Key_types key_types_all[] = {
 		{ "rsa1", "RSA1", _PATH_HOST_KEY_FILE },
 		{ "rsa", "RSA" ,_PATH_HOST_RSA_KEY_FILE },
 		{ "dsa", "DSA", _PATH_HOST_DSA_KEY_FILE },
 #ifdef OPENSSL_HAS_ECC
 		{ "ecdsa", "ECDSA",_PATH_HOST_ECDSA_KEY_FILE },
 #endif
 		{ "ed25519", "ED25519",_PATH_HOST_ED25519_KEY_FILE },
 		{ NULL, NULL, NULL }
 	};
+	struct Key_types key_types_fips140_2[] = {
+		{ "rsa", "RSA" ,_PATH_HOST_RSA_KEY_FILE },
+#ifdef OPENSSL_HAS_ECC
+		{ "ecdsa", "ECDSA",_PATH_HOST_ECDSA_KEY_FILE },
+#endif
+		{ NULL, NULL, NULL }
+	};
+	struct Key_types *key_types;
 
 	int first = 0;
 	struct stat st;
 	Key *private, *public;
 	char comment[1024];
 	int i, type, fd;
 	FILE *f;
 
+	if (fips_mode()) {
+		key_types = key_types_fips140_2;
+	} else {
+		key_types = key_types_all;
+	}
+
 	for (i = 0; key_types[i].key_type; i++) {
 		if (stat(key_types[i].path, &st) == 0)
 			continue;
 		if (errno != ENOENT) {
 			printf("Could not stat %s: %s", key_types[i].path,
 			    strerror(errno));
 			first = 0;
 			continue;
@@ -2570,16 +2587,23 @@ main(int argc, char **argv)
 		do_gen_all_hostkeys(pw);
 		return (0);
 	}
 
 	if (key_type_name == NULL)
 		key_type_name = "rsa";
 
 	type = key_type_from_name(key_type_name);
+
+	/* protocol v1 is not allowed in FIPS mode, DSA is not acceptable because
+	 * it has to be 1024 bit due to RFC 4253 using SHA-1 which implies 1024 bit
+	 * keys due to FIPS-186 specification for DSS */
+	if (fips_mode() && (type == KEY_RSA1 || type == KEY_DSA))
+		fatal("Key type %s not alowed in FIPS mode", key_type_name);
+	
 	type_bits_valid(type, &bits);
 
 	if (!quiet)
 		printf("Generating public/private %s key pair.\n", key_type_name);
 	private = key_generate(type, bits);
 	if (private == NULL) {
 		fprintf(stderr, "key_generate failed\n");
 		exit(1);
diff --git a/openssh-6.6p1/ssh.c b/openssh-6.6p1/ssh.c
--- a/openssh-6.6p1/ssh.c
+++ b/openssh-6.6p1/ssh.c
@@ -100,16 +100,18 @@
 #include "mac.h"
 #include "sshpty.h"
 #include "match.h"
 #include "msg.h"
 #include "uidswap.h"
 #include "roaming.h"
 #include "version.h"
 
+#include "fips.h"
+
 #ifdef ENABLE_PKCS11
 #include "ssh-pkcs11.h"
 #endif
 
 extern char *__progname;
 
 /* Saves a copy of argv for setproctitle emulation */
 #ifndef HAVE_SETPROCTITLE
@@ -499,16 +501,18 @@ main(int ac, char **av)
 	logfile = NULL;
 	argv0 = av[0];
 
  again:
 	while ((opt = getopt(ac, av, "1246ab:c:e:fgi:kl:m:no:p:qstvx"
 	    "ACD:E:F:I:KL:MNO:PQ:R:S:TVw:W:XYy")) != -1) {
 		switch (opt) {
 		case '1':
+			if (fips_mode())
+				fatal("Protocol 1 not allowed in the FIPS mode.");
 			options.protocol = SSH_PROTO_1;
 			break;
 		case '2':
 			options.protocol = SSH_PROTO_2;
 			break;
 		case '4':
 			options.address_family = AF_INET;
 			break;
@@ -927,16 +931,22 @@ main(int ac, char **av)
 	if (strcasecmp(host_arg, host) != 0) {
 		debug("Hostname has changed; re-reading configuration");
 		process_config_files(pw);
 	}
 
 	/* Fill configuration defaults. */
 	fill_default_options(&options);
 
+	if (FIPS_mode()) {
+		options.protocol &= SSH_PROTO_2;
+		if (options.protocol == 0)
+			fatal("Protocol 2 disabled by configuration but required in the FIPS mode");
+	}
+
 	if (options.port == 0)
 		options.port = default_ssh_port();
 	channel_set_af(options.address_family);
 
 	/* Tidy and check options */
 	if (options.host_key_alias != NULL)
 		lowercase(options.host_key_alias);
 	if (options.proxy_command != NULL &&
diff --git a/openssh-6.6p1/sshconnect2.c b/openssh-6.6p1/sshconnect2.c
--- a/openssh-6.6p1/sshconnect2.c
+++ b/openssh-6.6p1/sshconnect2.c
@@ -66,16 +66,18 @@
 #include "match.h"
 #include "dispatch.h"
 #include "canohost.h"
 #include "msg.h"
 #include "pathnames.h"
 #include "uidswap.h"
 #include "hostfile.h"
 
+#include "fips.h"
+
 #ifdef GSSAPI
 #include "ssh-gss.h"
 #endif
 
 /* import */
 extern char *client_version_string;
 extern char *server_version_string;
 extern Options options;
@@ -163,43 +165,55 @@ ssh_kex2(char *host, struct sockaddr *ho
 
 	if (options.ciphers == (char *)-1) {
 		logit("No valid ciphers for protocol version 2 given, using defaults.");
 		options.ciphers = NULL;
 	}
 	if (options.ciphers != NULL) {
 		myproposal[PROPOSAL_ENC_ALGS_CTOS] =
 		myproposal[PROPOSAL_ENC_ALGS_STOC] = options.ciphers;
+	} else if (fips_mode()) {
+        /* TODO: use intersection of FIPS ciphers and those requested in
+         * configuration */
+		myproposal[PROPOSAL_ENC_ALGS_CTOS] =
+		myproposal[PROPOSAL_ENC_ALGS_STOC] = KEX_DEFAULT_ENCRYPT_FIPS140_2;
 	}
 	myproposal[PROPOSAL_ENC_ALGS_CTOS] =
 	    compat_cipher_proposal(myproposal[PROPOSAL_ENC_ALGS_CTOS]);
 	myproposal[PROPOSAL_ENC_ALGS_STOC] =
 	    compat_cipher_proposal(myproposal[PROPOSAL_ENC_ALGS_STOC]);
 	if (options.compression) {
 		myproposal[PROPOSAL_COMP_ALGS_CTOS] =
 		myproposal[PROPOSAL_COMP_ALGS_STOC] = "zlib@openssh.com,zlib,none";
 	} else {
 		myproposal[PROPOSAL_COMP_ALGS_CTOS] =
 		myproposal[PROPOSAL_COMP_ALGS_STOC] = "none,zlib@openssh.com,zlib";
 	}
 	if (options.macs != NULL) {
 		myproposal[PROPOSAL_MAC_ALGS_CTOS] =
 		myproposal[PROPOSAL_MAC_ALGS_STOC] = options.macs;
+	} else if (fips_mode()) {
+        /* TODO: use intersection of FIPS macs and those requested in
+         * configuration */
+		myproposal[PROPOSAL_MAC_ALGS_CTOS] =
+		myproposal[PROPOSAL_MAC_ALGS_STOC] = KEX_DEFAULT_MAC_FIPS140_2;
 	}
 	if (options.hostkeyalgorithms != NULL)
 		myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] =
 		    compat_pkalg_proposal(options.hostkeyalgorithms);
 	else {
 		/* Prefer algorithms that we already have keys for */
 		myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] =
 		    compat_pkalg_proposal(
 		    order_hostkeyalgs(host, hostaddr, port));
 	}
 	if (options.kex_algorithms != NULL)
 		myproposal[PROPOSAL_KEX_ALGS] = options.kex_algorithms;
+	else if (fips_mode())
+		myproposal[PROPOSAL_KEX_ALGS] = KEX_DEFAULT_KEX_FIPS140_2;
 	myproposal[PROPOSAL_KEX_ALGS] = compat_kex_proposal(
 	    myproposal[PROPOSAL_KEX_ALGS]);
 
 	if (options.rekey_limit || options.rekey_interval)
 		packet_set_rekey_limits((u_int32_t)options.rekey_limit,
 		    (time_t)options.rekey_interval);
 
 	/* start key exchange */
diff --git a/openssh-6.6p1/sshd.c b/openssh-6.6p1/sshd.c
--- a/openssh-6.6p1/sshd.c
+++ b/openssh-6.6p1/sshd.c
@@ -117,16 +117,18 @@
 #ifdef GSSAPI
 #include "ssh-gss.h"
 #endif
 #include "monitor_wrap.h"
 #include "roaming.h"
 #include "ssh-sandbox.h"
 #include "version.h"
 
+#include "fips.h"
+
 #ifdef LIBWRAP
 #include <tcpd.h>
 #include <syslog.h>
 int allow_severity;
 int deny_severity;
 #endif /* LIBWRAP */
 
 #ifndef O_NOCTTY
@@ -1723,16 +1725,20 @@ main(int ac, char **av)
 		case KEY_ECDSA:
 		case KEY_ED25519:
 			sensitive_data.have_ssh2_key = 1;
 			break;
 		}
 		debug("private host key: #%d type %d %s", i, keytype,
 		    key_type(key ? key : pubkey));
 	}
+	if ((options.protocol & SSH_PROTO_1) && fips_mode()) {
+		logit("Disabling protocol version 1. Not allowed in the FIPS mode.");
+		options.protocol &= ~SSH_PROTO_1;
+	}
 	if ((options.protocol & SSH_PROTO_1) && !sensitive_data.have_ssh1_key) {
 		logit("Disabling protocol version 1. Could not load host key");
 		options.protocol &= ~SSH_PROTO_1;
 	}
 	if ((options.protocol & SSH_PROTO_2) && !sensitive_data.have_ssh2_key) {
 		logit("Disabling protocol version 2. Could not load host key");
 		options.protocol &= ~SSH_PROTO_2;
 	}
@@ -2370,30 +2376,30 @@ do_ssh1_kex(void)
 	}
 	if (rsafail) {
 		int bytes = BN_num_bytes(session_key_int);
 		u_char *buf = xmalloc(bytes);
 		struct ssh_digest_ctx *md;
 
 		logit("do_connection: generating a fake encryption key");
 		BN_bn2bin(session_key_int, buf);
-		if ((md = ssh_digest_start(SSH_DIGEST_MD5)) == NULL ||
+		if ((md = ssh_digest_start(fips_correct_dgst(SSH_DIGEST_MD5))) == NULL ||
 		    ssh_digest_update(md, buf, bytes) < 0 ||
 		    ssh_digest_update(md, sensitive_data.ssh1_cookie,
 		    SSH_SESSION_KEY_LENGTH) < 0 ||
 		    ssh_digest_final(md, session_key, sizeof(session_key)) < 0)
-			fatal("%s: md5 failed", __func__);
+			fatal("%s: hash failed", __func__);
 		ssh_digest_free(md);
-		if ((md = ssh_digest_start(SSH_DIGEST_MD5)) == NULL ||
+		if ((md = ssh_digest_start(fips_correct_dgst(SSH_DIGEST_MD5))) == NULL ||
 		    ssh_digest_update(md, session_key, 16) < 0 ||
 		    ssh_digest_update(md, sensitive_data.ssh1_cookie,
 		    SSH_SESSION_KEY_LENGTH) < 0 ||
 		    ssh_digest_final(md, session_key + 16,
 		    sizeof(session_key) - 16) < 0)
-			fatal("%s: md5 failed", __func__);
+			fatal("%s: hash failed", __func__);
 		ssh_digest_free(md);
 		explicit_bzero(buf, bytes);
 		free(buf);
 		for (i = 0; i < 16; i++)
 			session_id[i] = session_key[i] ^ session_key[i + 16];
 	}
 	/* Destroy the private and public keys. No longer. */
 	destroy_sensitive_data();
@@ -2441,35 +2447,43 @@ sshd_hostkey_sign(Key *privkey, Key *pub
 static void
 do_ssh2_kex(void)
 {
 	Kex *kex;
 
 	if (options.ciphers != NULL) {
 		myproposal[PROPOSAL_ENC_ALGS_CTOS] =
 		myproposal[PROPOSAL_ENC_ALGS_STOC] = options.ciphers;
+	} else if (fips_mode()) {
+		myproposal[PROPOSAL_ENC_ALGS_CTOS] =
+		myproposal[PROPOSAL_ENC_ALGS_STOC] = KEX_DEFAULT_ENCRYPT_FIPS140_2;
 	}
 	myproposal[PROPOSAL_ENC_ALGS_CTOS] =
 	    compat_cipher_proposal(myproposal[PROPOSAL_ENC_ALGS_CTOS]);
 	myproposal[PROPOSAL_ENC_ALGS_STOC] =
 	    compat_cipher_proposal(myproposal[PROPOSAL_ENC_ALGS_STOC]);
 
 	if (options.macs != NULL) {
 		myproposal[PROPOSAL_MAC_ALGS_CTOS] =
 		myproposal[PROPOSAL_MAC_ALGS_STOC] = options.macs;
+	} else if (fips_mode()) {
+		myproposal[PROPOSAL_MAC_ALGS_CTOS] =
+		myproposal[PROPOSAL_MAC_ALGS_STOC] = KEX_DEFAULT_MAC_FIPS140_2;
 	}
 	if (options.compression == COMP_NONE) {
 		myproposal[PROPOSAL_COMP_ALGS_CTOS] =
 		myproposal[PROPOSAL_COMP_ALGS_STOC] = "none";
 	} else if (options.compression == COMP_DELAYED) {
 		myproposal[PROPOSAL_COMP_ALGS_CTOS] =
 		myproposal[PROPOSAL_COMP_ALGS_STOC] = "none,zlib@openssh.com";
 	}
 	if (options.kex_algorithms != NULL)
 		myproposal[PROPOSAL_KEX_ALGS] = options.kex_algorithms;
+	else if (fips_mode())
+		myproposal[PROPOSAL_KEX_ALGS] = KEX_DEFAULT_KEX_FIPS140_2;
 
 	myproposal[PROPOSAL_KEX_ALGS] = compat_kex_proposal(
 	    myproposal[PROPOSAL_KEX_ALGS]);
 
 	if (options.rekey_limit || options.rekey_interval)
 		packet_set_rekey_limits((u_int32_t)options.rekey_limit,
 		    (time_t)options.rekey_interval);
 
