Blob Blame History Raw
diff -Naur libreswan-3.27-orig/include/alg_byname.h libreswan-3.27/include/alg_byname.h
--- libreswan-3.27-orig/include/alg_byname.h	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/include/alg_byname.h	2019-02-15 16:32:28.971835267 -0500
@@ -30,26 +30,26 @@
  * messages better align with the input files.
  */
 
-bool alg_byname_ok(const struct proposal_parser *parser,
+bool alg_byname_ok(struct proposal_parser *parser,
 		   const struct ike_alg *alg, shunk_t print_name);
 
 /*
  * Helper functions to implement most of the lookup.
  */
 
-const struct ike_alg *encrypt_alg_byname(const struct proposal_parser *parser,
+const struct ike_alg *encrypt_alg_byname(struct proposal_parser *parser,
 					 shunk_t name, size_t key_bit_length,
 					 shunk_t print_name);
 
-const struct ike_alg *prf_alg_byname(const struct proposal_parser *parser,
+const struct ike_alg *prf_alg_byname(struct proposal_parser *parser,
 				     shunk_t name, size_t key_bit_length,
 				     shunk_t print_name);
 
-const struct ike_alg *integ_alg_byname(const struct proposal_parser *parser,
+const struct ike_alg *integ_alg_byname(struct proposal_parser *parser,
 				       shunk_t name, size_t key_bit_length,
 				       shunk_t print_name);
 
-const struct ike_alg *dh_alg_byname(const struct proposal_parser *parser,
+const struct ike_alg *dh_alg_byname(struct proposal_parser *parser,
 				    shunk_t name, size_t key_bit_length,
 				    shunk_t print_name);
 
diff -Naur libreswan-3.27-orig/include/alg_info.h libreswan-3.27/include/alg_info.h
--- libreswan-3.27-orig/include/alg_info.h	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/include/alg_info.h	1969-12-31 19:00:00.000000000 -0500
@@ -1,245 +0,0 @@
-/* Algorithm info parsing and creation functions
- *
- * Author: JuanJo Ciarlante <jjo-ipsec@mendoza.gov.ar>
- * Copyright (C) 2007 Michael Richardson <mcr@xelerance.com>
- * Copyright (C) 2012-2013 Paul Wouters <paul@libreswan.org>
- * Copyright (C) 2013 D. Hugh Redelmeier <hugh@mimosa.com>
- * Copyright (C) 2013 Paul Wouters <pwouters@redhat.com>
- * Copyright (C) 2015-2017 Andrew Cagney
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.  See <https://www.gnu.org/licenses/gpl2.txt>.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * for more details.
- */
-
-#ifndef ALG_INFO_H
-#define ALG_INFO_H
-
-#include "lswcdefs.h"
-#include "constants.h"
-#include "ike_alg.h"
-#include "shunk.h"
-
-struct alg_info;
-struct proposal_protocol;
-struct proposal_info;
-struct proposal_policy;
-struct proposal_parser;
-
-typedef const struct ike_alg *(alg_byname_fn)(const struct proposal_parser *parser,
-					      shunk_t name, size_t key_bit_length,
-					      shunk_t print_name);
-
-/*
- * Parameters to tune the parser.
- */
-
-struct proposal_policy {
-	bool ikev1;
-	bool ikev2;
-	bool pfs; /* For CHILD SA, use DH from IKE SA */
-	/*
-	 * According to current policy, is the algorithm ok
-	 * (supported)?  If it isn't return FALSE.
-	 *
-	 * For instance, an IKE algorithm requires in-process support;
-	 * while an ESP/AH algorithm requires kernel support.
-	 */
-	bool (*alg_is_ok)(const struct ike_alg *alg);
-	/*
-	 * Print a warning.  Signature needs to match libreswan_log.
-	 */
-	int (*warning)(const char *fmt, ...) PRINTF_LIKE(1);
-};
-
-/*
- * Defaults to fill in.
- */
-
-struct proposal_defaults {
-	const struct ike_alg **dh;
-	const struct ike_alg **prf;
-	const struct ike_alg **integ;
-	const struct ike_alg **encrypt;
-};
-
-/*
- * Parameters to set the parser's basic behaviour - ESP/AH/IKE.
- */
-
-struct proposal_protocol {
-	const char *name;
-	enum ike_alg_key ikev1_alg_id;
-
-	/*
-	 * Lists of defaults.
-	 */
-	const struct proposal_defaults *ikev1_defaults;
-	const struct proposal_defaults *ikev2_defaults;
-
-	/*
-	 * Is the proposal OK?
-	 *
-	 * This is the final check, if this succeeds then the proposal
-	 * is added.
-	 */
-	bool (*proposal_ok)(const struct proposal_parser *parser,
-			    const struct proposal_info *proposal);
-
-	/*
-	 * XXX: Is the proto-id needed?  Parser should be protocol
-	 * agnostic.
-	 */
-	unsigned protoid;
-
-	/*
-	 * This lookup functions must set err and return null if NAME
-	 * isn't valid.
-	 */
-	alg_byname_fn *encrypt_alg_byname;
-	alg_byname_fn *prf_alg_byname;
-	alg_byname_fn *integ_alg_byname;
-	alg_byname_fn *dh_alg_byname;
-};
-
-/*
- * Everything combined.
- */
-struct proposal_parser {
-	const struct proposal_protocol *protocol;
-	const struct proposal_param *param;
-	const struct proposal_policy *policy;
-	char *err_buf;
-	size_t err_buf_len;
-};
-
-/*
- * A proposal as decoded by the parser.
- */
-
-struct proposal_info {
-	/*
-	 * The encryption algorithm and key length.
-	 *
-	 * Because struct encrypt_desc still specifies multiple key
-	 * lengths, ENCKEYLEN is still required.
-	 */
-	const struct encrypt_desc *encrypt;
-	size_t enckeylen;    /* keylength for ESP transform (bits) */
-	/*
-	 * The integrity and PRF algorithms.
-	 */
-	const struct prf_desc *prf;
-	const struct integ_desc *integ;
-	/*
-	 * PFS/DH negotiation.
-	 */
-	const struct oakley_group_desc *dh;
-	/*
-	 * Which protocol is this proposal intended for?
-	 */
-	const struct proposal_protocol *protocol;
-};
-
-/* common prefix of struct alg_info_esp and struct alg_info_ike */
-struct alg_info {
-	int alg_info_cnt;
-	int ref_cnt;
-	struct proposal_info proposals[128];
-};
-
-struct alg_info_esp {
-	struct alg_info ai;	/* common prefix */
-};
-
-struct alg_info_ike {
-	struct alg_info ai;	/* common prefix */
-};
-
-extern void alg_info_free(struct alg_info *alg_info);
-extern void alg_info_addref(struct alg_info *alg_info);
-extern void alg_info_delref(struct alg_info *alg_info);
-
-extern struct alg_info_ike *alg_info_ike_create_from_str(const struct proposal_policy *policy,
-							 const char *alg_str,
-							 char *err_buf, size_t err_buf_len);
-
-extern struct alg_info_esp *alg_info_esp_create_from_str(const struct proposal_policy *policy,
-							 const char *alg_str,
-							 char *err_buf, size_t err_buf_len);
-
-extern struct alg_info_esp *alg_info_ah_create_from_str(const struct proposal_policy *policy,
-							const char *alg_str,
-							char *err_buf, size_t err_buf_len);
-
-size_t lswlog_proposal_info(struct lswlog *log, const struct proposal_info *proposal);
-size_t lswlog_alg_info(struct lswlog *log, const struct alg_info *alg_info);
-
-/*
- * Iterate through the elements of an ESP or IKE table.
- *
- * Use __typeof__ instead of const to get around ALG_INFO some times
- * being const and sometimes not.
- *
- * XXX: yes, they are the same!
- */
-
-#define FOR_EACH_PROPOSAL_INFO(ALG_INFO, PROPOSAL_INFO)			\
-	for (__typeof__((ALG_INFO)->proposals[0]) *(PROPOSAL_INFO) = (ALG_INFO)->proposals; \
-	     (PROPOSAL_INFO) < (ALG_INFO)->proposals + (ALG_INFO)->alg_info_cnt; \
-	     (PROPOSAL_INFO)++)
-
-#define FOR_EACH_ESP_INFO(ALG_INFO, ESP_INFO)		\
-	FOR_EACH_PROPOSAL_INFO(&((ALG_INFO)->ai), ESP_INFO)
-
-#define FOR_EACH_IKE_INFO(ALG_INFO, IKE_INFO)		\
-	FOR_EACH_PROPOSAL_INFO(&((ALG_INFO)->ai), IKE_INFO)
-
-/*
- * Error indicated by err_buf[0] != '\0'.
- *
- * POLICY should be used to guard algorithm supported checks.  For
- * instance: if POLICY=IKEV1, then IKEv1 support is required (IKEv2 is
- * don't care); and if POLICY=IKEV1|IKEV2, then both IKEv1 and IKEv2
- * support is required.
- *
- * Parsing with POLICY=IKEV1, but then proposing the result using
- * IKEv2 is a program error.  The IKEv2 should complain loudly and,
- * we hope, not crash.
- *
- * Parsing with POLICY='0' is allowed. It will accept the algorithms
- * unconditionally (spi.c seems to need this).
- */
-
-struct proposal_parser proposal_parser(const struct proposal_policy *policy,
-				       const struct proposal_protocol *protocol,
-				       char *err_buf, size_t err_buf_len);
-
-bool alg_info_parse_str(const struct proposal_parser *parser,
-			struct alg_info *alg_info,
-			shunk_t alg_str);
-
-/*
- * Check that encrypt==AEAD and/or integ==none don't contradict.
- */
-bool proposal_aead_none_ok(const struct proposal_parser *parser,
-			   const struct proposal_info *proposal);
-
-bool alg_info_pfs_vs_dh_check(const struct proposal_parser *parser,
-			      struct alg_info_esp *aie);
-
-#if 0
-/* return true if it really is an error (when impaired return false) */
-bool proposal_error(const struct proposal_parser *parser,
-		    const char *message, ...) PRINTF_LIKE(2);
-#endif
-
-bool impair_proposal_errors(const struct proposal_parser *parser);
-
-#endif /* ALG_INFO_H */
diff -Naur libreswan-3.27-orig/include/ike_alg.h libreswan-3.27/include/ike_alg.h
--- libreswan-3.27-orig/include/ike_alg.h	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/include/ike_alg.h	2019-02-15 16:32:28.972835277 -0500
@@ -252,7 +252,7 @@
 	 * Easier to just require that this contain everything then
 	 * poke around in multiple places.
 	 */
-	const char *names[5];
+	const char *names[6];
 	/*
 	 * See above.
 	 *
diff -Naur libreswan-3.27-orig/include/lswlog.h libreswan-3.27/include/lswlog.h
--- libreswan-3.27-orig/include/lswlog.h	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/include/lswlog.h	2019-02-15 16:32:28.973835286 -0500
@@ -577,7 +577,7 @@
 #define PEXPECT_LOG(FMT, ...)						\
 	libreswan_pexpect_log(__func__,					\
 			      PASSERT_BASENAME, __LINE__,		\
-			      FMT, __VA_ARGS__)
+			      FMT,##__VA_ARGS__)
 
 
 /*
diff -Naur libreswan-3.27-orig/include/names_constant.h libreswan-3.27/include/names_constant.h
--- libreswan-3.27-orig/include/names_constant.h	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/include/names_constant.h	2019-02-15 16:32:28.973835286 -0500
@@ -41,6 +41,8 @@
 extern enum_names auth_alg_names;
 extern enum_names oakley_lifetime_names;
 
+extern enum_names ike_version_names;
+extern enum_names ike_version_liveness_names;
 extern enum_names version_names;
 extern enum_names doi_names;
 extern enum_names ikev1_payload_names;
diff -Naur libreswan-3.27-orig/include/pluto_constants.h libreswan-3.27/include/pluto_constants.h
--- libreswan-3.27-orig/include/pluto_constants.h	2019-02-15 16:31:43.034408076 -0500
+++ libreswan-3.27/include/pluto_constants.h	2019-02-15 16:32:28.974835295 -0500
@@ -28,6 +28,12 @@
 #  define DEFAULT_DNSSEC_ROOTKEY_FILE "<unused>"
 # endif
 
+enum ike_version {
+	IKEv1 = 1,
+	IKEv2 = 2,
+#define IKE_VERSION_ROOF 3
+};
+
 /*
  * IETF has no recommendations
  * FIPS SP800-77 sayas IKE max is 24h, IPsec max is 8h
diff -Naur libreswan-3.27-orig/include/proposals.h libreswan-3.27/include/proposals.h
--- libreswan-3.27-orig/include/proposals.h	1969-12-31 19:00:00.000000000 -0500
+++ libreswan-3.27/include/proposals.h	2019-02-15 16:32:28.975835304 -0500
@@ -0,0 +1,264 @@
+/* Proposals, for libreswan
+ *
+ * Author: JuanJo Ciarlante <jjo-ipsec@mendoza.gov.ar>
+ * Copyright (C) 2007 Michael Richardson <mcr@xelerance.com>
+ * Copyright (C) 2012-2013 Paul Wouters <paul@libreswan.org>
+ * Copyright (C) 2013 D. Hugh Redelmeier <hugh@mimosa.com>
+ * Copyright (C) 2013 Paul Wouters <pwouters@redhat.com>
+ * Copyright (C) 2015-2019 Andrew Cagney
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <https://www.gnu.org/licenses/gpl2.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+#ifndef PROPOSALS_H
+#define PROPOSALS_H
+
+#include "lswcdefs.h"
+#include "constants.h"
+#include "ike_alg.h"
+#include "shunk.h"
+
+struct alg_info;
+struct proposal_protocol;
+struct proposal;
+struct proposals;
+struct proposal_policy;
+struct proposal_parser;
+
+/*
+ * XXX: needs to be merged with IKE_ALG_TYPE.
+ */
+enum proposal_algorithm {
+	PROPOSAL_encrypt,
+	PROPOSAL_prf,
+	PROPOSAL_integ,
+	PROPOSAL_dh,
+	PROPOSAL_ALGORITHM_ROOF,
+};
+
+/*
+ * Everything combined.
+ */
+struct proposal_parser {
+	const struct proposal_protocol *protocol;
+	const struct proposal_param *param;
+	const struct proposal_policy *policy;
+	/* need to eliminate hardwired size */
+	char error[200];
+};
+
+/*
+ * Parameters to tune the parser.
+ */
+
+struct proposal_policy {
+	enum ike_version version;
+	unsigned parser_version;
+	bool pfs; /* For CHILD SA, use DH from IKE SA */
+	bool check_pfs_vs_dh;
+	/*
+	 * According to current policy, is the algorithm ok
+	 * (supported)?  If it isn't return FALSE.
+	 *
+	 * For instance, an IKE algorithm requires in-process support;
+	 * while an ESP/AH algorithm requires kernel support.
+	 */
+	bool (*alg_is_ok)(const struct ike_alg *alg);
+	/*
+	 * Print a warning.  Signature needs to match libreswan_log.
+	 */
+	int (*warning)(const char *fmt, ...) PRINTF_LIKE(1);
+};
+
+/*
+ * Defaults the parser uses to fill things in.
+ */
+
+struct proposal_defaults {
+	const struct ike_alg **dh;
+	const struct ike_alg **prf;
+	const struct ike_alg **integ;
+	const struct ike_alg **encrypt;
+};
+
+/*
+ * The protocol - ESP/AH/IKE - the parser is processing.
+ */
+
+typedef const struct ike_alg *(alg_byname_fn)(struct proposal_parser *parser,
+					      shunk_t name, size_t key_bit_length,
+					      shunk_t print_name);
+
+struct proposal_protocol {
+	const char *name;
+	enum ike_alg_key ikev1_alg_id;
+
+	/*
+	 * Lists of defaults for each IKE version.
+	 */
+	const struct proposal_defaults *defaults[IKE_VERSION_ROOF];
+
+	/*
+	 * Is the proposal OK?
+	 *
+	 * This is the final check, if this succeeds then the proposal
+	 * is added.
+	 */
+	bool (*proposal_ok)(struct proposal_parser *parser,
+			    const struct proposal *proposal);
+
+	/*
+	 * XXX: Is the proto-id needed?  Parser should be protocol
+	 * agnostic.
+	 */
+	unsigned protoid;
+
+	/*
+	 * This lookup functions must set err and return null if NAME
+	 * isn't valid.
+	 */
+	alg_byname_fn *encrypt_alg_byname;
+	alg_byname_fn *prf_alg_byname;
+	alg_byname_fn *integ_alg_byname;
+	alg_byname_fn *dh_alg_byname;
+};
+
+/*
+ * A proposal as decoded by the parser.
+ */
+
+struct algorithm {
+	const struct ike_alg *desc;
+	/*
+	 * Because struct encrypt_desc still specifies multiple key
+	 * lengths, ENCKEYLEN is still required.
+	 */
+	int enckeylen; /* only one! */
+	struct algorithm *next;
+};
+
+/* return counts of encrypt=aead and integ=none */
+bool proposal_encrypt_aead(const struct proposal *proposal);
+bool proposal_encrypt_norm(const struct proposal *proposal);
+bool proposal_integ_none(const struct proposal *proposal);
+
+unsigned nr_proposals(struct proposals *proposals);
+
+extern void proposals_addref(struct proposals **proposals);
+extern void proposals_delref(struct proposals **proposals);
+
+extern struct proposal *alloc_proposal(struct proposal_parser *parser);
+extern void free_proposal(struct proposal **proposal);
+
+void free_algorithms(struct proposal *proposal, enum proposal_algorithm algorithm);
+void append_proposal(struct proposals *proposals, struct proposal **proposal);
+void append_algorithm(struct proposal_parser *parser,
+		      struct proposal *proposal, enum proposal_algorithm algorithm,
+		      const struct ike_alg *alg, int enckeylen);
+
+struct proposal_parser *alloc_proposal_parser(const struct proposal_policy *policy,
+					      const struct proposal_protocol *protocol);
+void free_proposal_parser(struct proposal_parser **parser);
+struct proposal_parser *ike_proposal_parser(const struct proposal_policy *policy);
+struct proposal_parser *esp_proposal_parser(const struct proposal_policy *policy);
+struct proposal_parser *ah_proposal_parser(const struct proposal_policy *policy);
+
+/*
+ * XXX: useful?
+ */
+struct ike_proposals {
+	struct proposals *p;
+};
+
+struct child_proposals {
+	struct proposals *p;
+};
+
+void fmt_proposal(struct lswlog *log,
+		  const struct proposal *proposal);
+void fmt_proposals(struct lswlog *log, const struct proposals *proposals);
+
+/*
+ * Iterate through all the proposals and the proposal's algorithms.
+ *
+ * Use __typeof__ instead of const to get around ALG_INFO some times
+ * being const and sometimes not.
+ */
+
+struct proposal *next_proposal(const struct proposals *proposals,
+				     struct proposal *last_proposal);
+
+#define FOR_EACH_PROPOSAL(PROPOSALS, PROPOSAL)				\
+	for (struct proposal *PROPOSAL = next_proposal(PROPOSALS, NULL); \
+	     PROPOSAL != NULL;						\
+	     PROPOSAL = next_proposal(PROPOSALS, PROPOSAL))
+
+struct algorithm *next_algorithm(const struct proposal *proposal,
+				 enum proposal_algorithm algorithm,
+				 struct algorithm *last);
+
+#define FOR_EACH_ALGORITHM(PROPOSAL, TYPE, ALGORITHM)	\
+	for (struct algorithm *ALGORITHM = next_algorithm(PROPOSAL, PROPOSAL_##TYPE, NULL); \
+	     ALGORITHM != NULL; ALGORITHM = next_algorithm(PROPOSAL, PROPOSAL_##TYPE, ALGORITHM))
+
+/*
+ * Error indicated by err_buf[0] != '\0'.
+ *
+ * POLICY should be used to guard algorithm supported checks.  For
+ * instance: if POLICY=IKEV1, then IKEv1 support is required (IKEv2 is
+ * don't care); and if POLICY=IKEV1|IKEV2, then both IKEv1 and IKEv2
+ * support is required.
+ *
+ * Parsing with POLICY=IKEV1, but then proposing the result using
+ * IKEv2 is a program error.  The IKEv2 should complain loudly and,
+ * we hope, not crash.
+ *
+ * Parsing with POLICY='0' is allowed. It will accept the algorithms
+ * unconditionally (spi.c seems to need this).
+ */
+
+struct proposals *proposals_from_str(struct proposal_parser *parser,
+				     const char *str);
+
+bool v1_proposals_parse_str(struct proposal_parser *parser,
+			    struct proposals *proposals,
+			    shunk_t alg_str);
+bool v2_proposals_parse_str(struct proposal_parser *parser,
+			    struct proposals *proposals,
+			    shunk_t alg_str);
+
+/*
+ * Check that encrypt==AEAD and/or integ==none don't contradict.
+ */
+bool proposal_aead_none_ok(struct proposal_parser *parser,
+			   const struct proposal *proposal);
+
+void proposal_error(struct proposal_parser *parser,
+		    const char *message, ...) PRINTF_LIKE(2);
+
+bool impair_proposal_errors(struct proposal_parser *parser);
+
+/*
+ * Convert a generic proposal back into something the IKEv1 code can
+ * digest.
+ */
+struct v1_proposal {
+	int enckeylen;
+	const struct encrypt_desc *encrypt;
+	const struct prf_desc *prf;
+	const struct integ_desc *integ;
+	const struct oakley_group_desc *dh;
+	const struct proposal_protocol *protocol;
+};
+
+struct v1_proposal v1_proposal(const struct proposal *proposal);
+
+#endif /* PROPOSALS_H */
diff -Naur libreswan-3.27-orig/include/shunk.h libreswan-3.27/include/shunk.h
--- libreswan-3.27-orig/include/shunk.h	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/include/shunk.h	2019-02-15 16:32:28.975835304 -0500
@@ -1,7 +1,6 @@
-/*
- * string fragments, for libreswan
+/* constant string (octet) fragments, for libreswan
  *
- * Copyright (C) 2018 Andrew Cagney
+ * Copyright (C) 2018-2019 Andrew Cagney
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -21,8 +20,15 @@
 #include <stddef.h>	/* size_t */
 
 /*
- * shunk_t is a rip of of chunk_t, but with a character pointer.  It
- * is intended for string slicing.
+ * Think of shunk_t and shunk_t as opposite solutions to the same
+ * problem - carving up streams of octets:
+ *
+ * shunk_t's buffer is constant making it good for manipulating static
+ * constant data (such as "a string"), chunk_t's is not.
+ *
+ * shunk_t's buffer is of type 'char' (which may or may not be signed)
+ * making it easier to manipulate strings, chunk_t's is uint8_t making
+ * it easier to manipulate raw bytes.
  */
 
 struct shunk {
@@ -32,21 +38,49 @@
 
 typedef struct shunk shunk_t;
 
-extern const shunk_t empty_shunk;
+/*
+ * Just like for strings, an empty or zero length shunk such as
+ * {.ptr="",.len = 0} should not be confused with the NULL shunk
+ * (i.e., {.ptr=NULL,.len=0}).
+ *
+ * Use 'null_shunk' in initialisers.  The only exception is static
+ * initializers - which will get a compiler error - and NULL_SHUNK can
+ * be used.
+ */
+
+#define NULL_SHUNK { .ptr = NULL, .len = 0, }
+extern const shunk_t null_shunk;
 
 shunk_t shunk1(const char *ptr); /* strlen() implied */
 shunk_t shunk2(const char *ptr, int len);
 
 /*
- * shunk version of strsep() (which is like strtok())
+ * A shunk version of strsep() (which is like strtok()) - split INPUT
+ * in two using a character from the DELIM set.
+ *
+ * If INPUT contains a character from the DELIM set, return the
+ * characters before the DELIM character as the next TOKEN, and set
+ * INPUT to the sub-string following the DELIM character.
+ *
+ * If INPUT contains no character from the DELIM set, return INPUT as
+ * the next TOKEN (possibly empty), and set INPUT to the null_shunk.
+ *
+ * If INPUT is the null_shunk, return the null_shunk as the next
+ * TOKEN, string remains unchanged (still the null_shunk).
+ *
+ * One way to implement a simple parser is to use TOKEN.ptr==NULL as
+ * an end-of-input indicator:
  *
- * Split SHUNK in two using the DELIM set.  Return a shunk of the
- * characters up to but not including DELIM (or the entire string if
- * DELIM isn't found.  Update SHUNK to be one past DELIM.
+ *     shunk_t token = shunk_strsep(&input, ",");
+ *     while (token.ptr != NULL) {
+ *       ... process token ...
+ *       token = shunk_strsep(&input, ",");
+ *     }
  *
- * XXX: should this return the DELIM?
+ * XXX: Provided INPUT.ptr is non-NULL, INPUT.ptr[-1] is the DELIM
+ * character; should this be made an explict parameter.
  */
-shunk_t shunk_strsep(shunk_t *shunk, const char *delim);
+shunk_t shunk_strsep(shunk_t *input, const char *delim);
 
 /*
  * shunk version of string compare functions (or at least libreswan's
diff -Naur libreswan-3.27-orig/lib/libswan/ah_info.c libreswan-3.27/lib/libswan/ah_info.c
--- libreswan-3.27-orig/lib/libswan/ah_info.c	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/lib/libswan/ah_info.c	2019-02-15 16:50:12.776728076 -0500
@@ -22,50 +22,71 @@
 
 #include "lswalloc.h"
 #include "lswlog.h"
-#include "alg_info.h"
+#include "proposals.h"
 #include "alg_byname.h"
 #include "lswfips.h"
 
 #include "ike_alg.h"
 #include "ike_alg_integ.h"
 
-static bool ah_proposal_ok(const struct proposal_parser *parser,
-			   const struct proposal_info *proposal)
+static bool ah_proposal_ok(struct proposal_parser *parser,
+			   const struct proposal *proposal)
 {
-	impaired_passert(PROPOSAL_PARSER, proposal->encrypt == NULL);
-	impaired_passert(PROPOSAL_PARSER, proposal->prf == NULL);
-	impaired_passert(PROPOSAL_PARSER, proposal->integ != NULL);
+	impaired_passert(PROPOSAL_PARSER,
+			 next_algorithm(proposal, PROPOSAL_encrypt, NULL) == NULL);
+	impaired_passert(PROPOSAL_PARSER,
+			 next_algorithm(proposal, PROPOSAL_prf, NULL) == NULL);
+	impaired_passert(PROPOSAL_PARSER,
+			 next_algorithm(proposal, PROPOSAL_integ, NULL) != NULL);
 
 	/* ah=null is invalid */
-	if (!IMPAIR(ALLOW_NULL_NONE) &&
-	    proposal->integ == &ike_alg_integ_none) {
-		snprintf(parser->err_buf, parser->err_buf_len,
-			 "AH cannot have 'none' as the integrity algorithm");
-		if (!impair_proposal_errors(parser)) {
-			return false;
+	if (!IMPAIR(ALLOW_NULL_NONE)) {
+		FOR_EACH_ALGORITHM(proposal, integ, alg) {
+			/* passerts */
+			const struct integ_desc *integ = integ_desc(alg->desc);
+			if (integ == &ike_alg_integ_none) {
+				proposal_error(parser, "AH cannot have 'none' as the integrity algorithm");
+				if (!impair_proposal_errors(parser)) {
+					return false;
+				}
+			}
 		}
 	}
 
 	return true;
 }
 
-static const struct ike_alg *default_ah_integ[] = {
+static const struct ike_alg *default_v1_ah_integ[] = {
 #ifdef USE_SHA1
 	&ike_alg_integ_sha1.common,
 #endif
 	NULL,
 };
 
-const struct proposal_defaults ah_defaults = {
-	.integ = default_ah_integ,
+static const struct ike_alg *default_v2_ah_integ[] = {
+#ifdef USE_SHA2
+	&ike_alg_integ_sha2_512.common,
+	&ike_alg_integ_sha2_256.common,
+#endif
+	NULL,
+};
+
+const struct proposal_defaults v1_ah_defaults = {
+	.integ = default_v1_ah_integ,
+};
+
+const struct proposal_defaults v2_ah_defaults = {
+	.integ = default_v2_ah_integ,
 };
 
 const struct proposal_protocol ah_proposal_protocol = {
 	.name = "AH",
 	.ikev1_alg_id = IKEv1_ESP_ID,
 	.protoid = PROTO_IPSEC_AH,
-	.ikev1_defaults = &ah_defaults,
-	.ikev2_defaults = &ah_defaults,
+	.defaults = {
+		[IKEv1] = &v1_ah_defaults,
+		[IKEv2] = &v2_ah_defaults,
+	},
 	.proposal_ok = ah_proposal_ok,
 	.integ_alg_byname = integ_alg_byname,
 	.dh_alg_byname = dh_alg_byname,
@@ -77,7 +98,7 @@
  * parser configuration - encryption isn't allowed.
  *
  * ??? the only difference between
- * alg_info_ah_create_from_str and alg_info_esp_create_from_str
+ * ah_proposals_create_from_str and alg_info_esp_create_from_str
  * is in the second argument to proposal_parser.
  *
  * XXX: On the other hand, since "struct ike_info" and "struct
@@ -87,33 +108,7 @@
 
 /* This function is tested in testing/algparse/algparse.c */
 
-struct alg_info_esp *alg_info_ah_create_from_str(const struct proposal_policy *policy,
-						 const char *alg_str,
-						 char *err_buf, size_t err_buf_len)
+struct proposal_parser *ah_proposal_parser(const struct proposal_policy *policy)
 {
-	shunk_t string = shunk1(alg_str);
-	const struct proposal_parser parser = proposal_parser(policy,
-							      &ah_proposal_protocol,
-							      err_buf, err_buf_len);
-
-	/*
-	 * alg_info storage should be sized dynamically
-	 * but this may require two passes to know
-	 * transform count in advance.
-	 */
-	struct alg_info_esp *alg_info_ah = alloc_thing(struct alg_info_esp, "alg_info_ah");
-
-	if (!alg_info_parse_str(&parser, &alg_info_ah->ai, string)) {
-		passert(err_buf[0] != '\0');
-		alg_info_free(&alg_info_ah->ai);
-		return NULL;
-	}
-
-	if (!alg_info_pfs_vs_dh_check(&parser, alg_info_ah)) {
-		passert(err_buf[0] != '\0');
-		alg_info_free(&alg_info_ah->ai);
-		return NULL;
-	}
-
-	return alg_info_ah;
+	return alloc_proposal_parser(policy, &ah_proposal_protocol);
 }
diff -Naur libreswan-3.27-orig/lib/libswan/alg_byname.c libreswan-3.27/lib/libswan/alg_byname.c
--- libreswan-3.27-orig/lib/libswan/alg_byname.c	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/lib/libswan/alg_byname.c	2019-02-15 16:32:28.976835314 -0500
@@ -17,11 +17,11 @@
 #include <stdlib.h>
 
 #include "lswlog.h"
-#include "alg_info.h"
+#include "proposals.h"
 #include "alg_byname.h"
 #include "ike_alg.h"
 
-bool alg_byname_ok(const struct proposal_parser *parser,
+bool alg_byname_ok(struct proposal_parser *parser,
 		   const struct ike_alg *alg, shunk_t print_name)
 {
 	const struct proposal_protocol *protocol = parser->protocol;
@@ -30,19 +30,27 @@
 	 * If the connection is IKEv1|IKEv2 then this code will
 	 * exclude anything not supported by both protocols.
 	 */
-	if (policy->ikev1 && alg->id[protocol->ikev1_alg_id] < 0) {
-		snprintf(parser->err_buf, parser->err_buf_len,
-			 "%s %s algorithm '"PRI_SHUNK"' is not supported by IKEv1",
-			 protocol->name, ike_alg_type_name(alg->algo_type),
-			 PRI_shunk(print_name));
-		return false;
-	}
-	if (policy->ikev2 && alg->id[IKEv2_ALG_ID] < 0) {
-		snprintf(parser->err_buf, parser->err_buf_len,
-			 "%s %s algorithm '"PRI_SHUNK"' is not supported by IKEv2",
-			 protocol->name, ike_alg_type_name(alg->algo_type),
-			 PRI_shunk(print_name));
-		return false;
+       switch (policy->version) {
+        case IKEv1:
+                /* IKEv1 has different IDs for ESP/IKE/AH */
+                if (alg->id[protocol->ikev1_alg_id] < 0) {
+                        proposal_error(parser, "%s %s algorithm '"PRI_SHUNK"' is not supported by IKEv1",
+                                       protocol->name, ike_alg_type_name(alg->algo_type),
+                                       PRI_shunk(print_name));
+                        return false;
+                }
+                break;
+        case IKEv2:
+                if (alg->id[IKEv2_ALG_ID] < 0) {
+                        proposal_error(parser, "%s %s algorithm '"PRI_SHUNK"' is not supported by IKEv2",
+                                       protocol->name, ike_alg_type_name(alg->algo_type),
+                                       PRI_shunk(print_name));
+                        return false;
+                }
+                break;
+        default:
+                /* ignore */
+                break;
 	}
 	/*
 	 * According to parser policy, is the algorithm "implemented"?
@@ -54,10 +62,9 @@
 	 */
 	passert(policy->alg_is_ok != NULL);
 	if (!policy->alg_is_ok(alg)) {
-		snprintf(parser->err_buf, parser->err_buf_len,
-			 "%s %s algorithm '"PRI_SHUNK"' is not supported",
-			 protocol->name, ike_alg_type_name(alg->algo_type),
-			 PRI_shunk(print_name));
+		proposal_error(parser, "%s %s algorithm '"PRI_SHUNK"' is not supported",
+				protocol->name, ike_alg_type_name(alg->algo_type),
+				PRI_shunk(print_name));
 		return false;
 	}
 	/*
@@ -69,16 +76,15 @@
 	 * Since it likely involves a lookup, it is left until last.
 	 */
 	if (!ike_alg_is_valid(alg)) {
-		snprintf(parser->err_buf, parser->err_buf_len,
-			 "%s %s algorithm '"PRI_SHUNK"' is not valid",
-			 protocol->name, ike_alg_type_name(alg->algo_type),
-			 PRI_shunk(print_name));
+		proposal_error(parser, "%s %s algorithm '"PRI_SHUNK"' is not valid",
+				protocol->name, ike_alg_type_name(alg->algo_type),
+				PRI_shunk(print_name));
 		return false;
 	}
 	return true;
 }
 
-static const struct ike_alg *alg_byname(const struct proposal_parser *parser,
+static const struct ike_alg *alg_byname(struct proposal_parser *parser,
 					const struct ike_alg_type *type,
 					shunk_t name, shunk_t print_name)
 {
@@ -91,15 +97,14 @@
 		 */
 		if (ike_alg_enum_match(type, protocol->ikev1_alg_id, name) >= 0 ||
 		    ike_alg_enum_match(type, IKEv2_ALG_ID, name) >= 0) {
-			snprintf(parser->err_buf, parser->err_buf_len,
-				 "%s %s algorithm '"PRI_SHUNK"' is not supported",
-				 protocol->name, ike_alg_type_name(type),
-				 PRI_shunk(print_name));
+			proposal_error(parser, "%s %s algorithm '"PRI_SHUNK"' is not supported",
+				protocol->name, ike_alg_type_name(type),
+				PRI_shunk(print_name));
+
 		} else {
-			snprintf(parser->err_buf, parser->err_buf_len,
-				 "%s %s algorithm '"PRI_SHUNK"' is not recognized",
-				 protocol->name, ike_alg_type_name(type),
-				 PRI_shunk(print_name));
+			proposal_error(parser, "%s %s algorithm '"PRI_SHUNK"' is not recognized",
+				protocol->name, ike_alg_type_name(type),
+				PRI_shunk(print_name));
 		}
 		return NULL;
 	}
@@ -108,14 +113,14 @@
 	 * Does it pass muster?
 	 */
 	if (!alg_byname_ok(parser, alg, print_name)) {
-		passert(parser->err_buf[0] != '\0');
+		passert(parser->error[0] != '\0');
 		return NULL;
 	}
 
 	return alg;
 }
 
-const struct ike_alg *encrypt_alg_byname(const struct proposal_parser *parser,
+const struct ike_alg *encrypt_alg_byname(struct proposal_parser *parser,
 					 shunk_t name, size_t key_bit_length,
 					 shunk_t print_name)
 {
@@ -127,10 +132,9 @@
 	const struct encrypt_desc *encrypt = encrypt_desc(alg);
 	if (!IMPAIR(SEND_KEY_SIZE_CHECK) && key_bit_length > 0) {
 		if (encrypt->keylen_omitted) {
-			snprintf(parser->err_buf, parser->err_buf_len,
-				 "%s does not take variable key lengths",
-				 enum_short_name(&ikev2_trans_type_encr_names,
-						 encrypt->common.id[IKEv2_ALG_ID]));
+			proposal_error(parser, "%s does not take variable key lengths",
+					enum_short_name(&ikev2_trans_type_encr_names,
+					encrypt->common.id[IKEv2_ALG_ID]));
 			if (!impair_proposal_errors(parser)) {
 				return NULL;
 			}
@@ -141,8 +145,7 @@
 			 * should instead generate a real list from
 			 * encrypt.
 			 */
-			snprintf(parser->err_buf, parser->err_buf_len,
-				 "wrong encryption key length - key size must be 128 (default), 192 or 256");
+			proposal_error(parser, "wrong encryption key length - key size must be 128 (default), 192 or 256");
 			if (!impair_proposal_errors(parser)) {
 				return NULL;
 			}
@@ -151,21 +154,21 @@
 	return alg;
 }
 
-const struct ike_alg *prf_alg_byname(const struct proposal_parser *parser,
+const struct ike_alg *prf_alg_byname(struct proposal_parser *parser,
 				     shunk_t name, size_t key_bit_length UNUSED,
 				     shunk_t print_name)
 {
 	return alg_byname(parser, IKE_ALG_PRF, name, print_name);
 }
 
-const struct ike_alg *integ_alg_byname(const struct proposal_parser *parser,
+const struct ike_alg *integ_alg_byname(struct proposal_parser *parser,
 				       shunk_t name, size_t key_bit_length UNUSED,
 				       shunk_t print_name)
 {
 	return alg_byname(parser, IKE_ALG_INTEG, name, print_name);
 }
 
-const struct ike_alg *dh_alg_byname(const struct proposal_parser *parser,
+const struct ike_alg *dh_alg_byname(struct proposal_parser *parser,
 				    shunk_t name, size_t key_bit_length UNUSED,
 				    shunk_t print_name)
 {
diff -Naur libreswan-3.27-orig/lib/libswan/alg_info.c libreswan-3.27/lib/libswan/alg_info.c
--- libreswan-3.27-orig/lib/libswan/alg_info.c	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/lib/libswan/alg_info.c	1969-12-31 19:00:00.000000000 -0500
@@ -1,884 +0,0 @@
-/*
- * Algorithm info parsing and creation functions
- * Author: JuanJo Ciarlante <jjo-ipsec@mendoza.gov.ar>
- *
- * Copyright (C) 2012 Paul Wouters <paul@libreswan.org>
- * Copyright (C) 2015-2018 Andrew Cagney
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.  See <https://www.gnu.org/licenses/gpl2.txt>.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * for more details.
- */
-
-#include <stdio.h>
-#include <string.h>
-#include <limits.h>
-
-#include "lswlog.h"
-#include "lswalloc.h"
-#include "constants.h"
-#include "alg_info.h"
-#include "ike_alg.h"
-#include "ike_alg_integ.h"
-#include "ike_alg_dh.h"
-#include "alg_byname.h"
-
-/*
- * Add the proposal defaults for the specific algorithm.
- */
-
-typedef struct proposal_info merge_alg_default_t(struct proposal_info proposal,
-						 const struct ike_alg *default_alg);
-
-static struct proposal_info merge_dh_default(struct proposal_info proposal,
-					     const struct ike_alg *default_alg)
-{
-	proposal.dh = oakley_group_desc(default_alg);
-	return proposal;
-}
-
-static struct proposal_info merge_encrypt_default(struct proposal_info proposal,
-						  const struct ike_alg *default_alg)
-{
-	proposal.encrypt = encrypt_desc(default_alg);
-	return proposal;
-}
-
-static struct proposal_info merge_prf_default(struct proposal_info proposal,
-					      const struct ike_alg *default_alg)
-{
-	proposal.prf = prf_desc(default_alg);
-	return proposal;
-}
-
-static struct proposal_info merge_integ_default(struct proposal_info proposal,
-						const struct ike_alg *default_alg)
-{
-	proposal.integ = integ_desc(default_alg);
-	return proposal;
-}
-
-static bool add_proposal_defaults(const struct proposal_parser *parser,
-				  const struct proposal_defaults *defaults,
-				  struct alg_info *alg_info,
-				  const struct proposal_info *proposal);
-
-static bool add_alg_defaults(const struct proposal_parser *parser,
-			     const struct proposal_defaults *defaults,
-			     struct alg_info *alg_info,
-			     const struct proposal_info *proposal,
-			     const struct ike_alg_type *type,
-			     const struct ike_alg **default_algs,
-			     merge_alg_default_t *merge_alg_default)
-{
-	/*
-	 * Use VALID_ALG to add the valid algorithms into VALID_ALGS.
-	 */
-	for (const struct ike_alg **default_alg = default_algs;
-	     *default_alg; default_alg++) {
-		const struct ike_alg *alg = *default_alg;
-		if (!alg_byname_ok(parser, alg,
-				   shunk1(alg->name))) {
-			DBG(DBG_PROPOSAL_PARSER,
-			    DBG_log("skipping default %s",
-				    parser->err_buf));
-			parser->err_buf[0] = '\0';
-			continue;
-		}
-		/* add it */
-		DBG(DBG_PROPOSAL_PARSER,
-		    DBG_log("adding default %s %s",
-			    ike_alg_type_name(type),
-			    alg->name));
-		struct proposal_info merged_proposal = merge_alg_default(*proposal,
-									 *default_alg);
-		if (!add_proposal_defaults(parser, defaults,
-					   alg_info, &merged_proposal)) {
-			passert(parser->err_buf[0] != '\0');
-			return false;
-		}
-	}
-	return true;
-}
-
-/*
- * Validate the proposal and, suppressing duplicates, add it to the
- * proposal list.
- */
-
-static bool add_proposal(const struct proposal_parser *parser,
-			 struct alg_info *alg_info,
-			 const struct proposal_info *proposal)
-{
-	/* duplicate? */
-	FOR_EACH_PROPOSAL_INFO(alg_info, existing_proposal) {
-		/*
-		 * key length 0 is like a wild-card (it actually means
-		 * propose default and strongest key lengths) so if
-		 * either is zero just treat it as a match.
-		 */
-		if (existing_proposal->encrypt == proposal->encrypt &&
-		    existing_proposal->prf == proposal->prf &&
-		    existing_proposal->integ == proposal->integ &&
-		    existing_proposal->dh == proposal->dh &&
-		    (existing_proposal->enckeylen == proposal->enckeylen ||
-		     existing_proposal->enckeylen == 0 ||
-		     proposal->enckeylen == 0)) {
-			if (IMPAIR(PROPOSAL_PARSER)) {
-				libreswan_log("IMPAIR: including duplicate %s proposal encrypt=%s enckeylen=%zu prf=%s integ=%s dh=%s",
-					      proposal->protocol->name,
-					      proposal->encrypt != NULL ? proposal->encrypt->common.name : "n/a",
-					      proposal->enckeylen,
-					      proposal->prf != NULL ? proposal->prf->common.name : "n/a",
-					      proposal->integ != NULL ? proposal->integ->common.name : "n/a",
-					      proposal->dh != NULL ? proposal->dh->common.name : "n/a");
-			} else {
-				DBG(DBG_CRYPT,
-				    DBG_log("discarding duplicate %s proposal encrypt=%s enckeylen=%zu prf=%s integ=%s dh=%s",
-					    proposal->protocol->name,
-					    proposal->encrypt != NULL ? proposal->encrypt->common.name : "n/a",
-					    proposal->enckeylen,
-					    proposal->prf != NULL ? proposal->prf->common.name : "n/a",
-					    proposal->integ != NULL ? proposal->integ->common.name : "n/a",
-					    proposal->dh != NULL ? proposal->dh->common.name : "n/a"));
-				return true;
-			}
-		}
-	}
-
-	/* Overflow? */
-	if ((unsigned)alg_info->alg_info_cnt >= elemsof(alg_info->proposals)) {
-		snprintf(parser->err_buf, parser->err_buf_len,
-			 "more than %zu %s algorithms specified",
-			 elemsof(alg_info->proposals),
-			 proposal->protocol->name);
-		/* drop it like a rock */
-		return false;
-	}
-
-	/* back end? */
-	if (!proposal->protocol->proposal_ok(parser, proposal)) {
-		return false;
-	}
-
-	alg_info->proposals[alg_info->alg_info_cnt++] = *proposal;
-	return true;
-}
-
-/*
- * For all the algorithms, when an algorithm is missing (NULL), and
- * there are defaults, add them.
- */
-
-static bool add_proposal_defaults(const struct proposal_parser *parser,
-				  const struct proposal_defaults *defaults,
-				  struct alg_info *alg_info,
-				  const struct proposal_info *proposal)
-{
-	/*
-	 * Note that the order in which things are recursively added -
-	 * MODP, ENCR, PRF/HASH - affects test results.  It determines
-	 * things like the order of proposals.
-	 */
-	if (proposal->dh == NULL &&
-	    defaults != NULL && defaults->dh != NULL) {
-		return add_alg_defaults(parser, defaults,
-					alg_info, proposal,
-					&ike_alg_dh, defaults->dh,
-					merge_dh_default);
-	} else if (proposal->encrypt == NULL &&
-		   defaults != NULL && defaults->encrypt != NULL) {
-		return add_alg_defaults(parser, defaults,
-					alg_info, proposal,
-					&ike_alg_encrypt, defaults->encrypt,
-					merge_encrypt_default);
-	} else if (proposal->prf == NULL &&
-		   defaults != NULL && defaults->prf != NULL) {
-		return add_alg_defaults(parser, defaults,
-					alg_info, proposal,
-					&ike_alg_prf, defaults->prf,
-					merge_prf_default);
-	} else if (proposal->integ == NULL &&
-		   proposal->encrypt != NULL &&
-		   encrypt_desc_is_aead(proposal->encrypt)) {
-		/*
-		 * Since AEAD, integrity is always 'none'.
-		 */
-		struct proposal_info merged_proposal = *proposal;
-		merged_proposal.integ = &ike_alg_integ_none;
-		return add_proposal_defaults(parser, defaults,
-					     alg_info, &merged_proposal);
-	} else if (proposal->integ == NULL &&
-		   defaults != NULL && defaults->integ != NULL) {
-		return add_alg_defaults(parser, defaults,
-					alg_info, proposal,
-					&ike_alg_integ, defaults->integ,
-					merge_integ_default);
-	} else if (proposal->integ == NULL &&
-		   proposal->prf != NULL &&
-		   proposal->encrypt != NULL &&
-		   !encrypt_desc_is_aead(proposal->encrypt)) {
-		/*
-		 * Since non-AEAD, use an integrity algorithm that is
-		 * implemented using the PRF.
-		 */
-		struct proposal_info merged_proposal = *proposal;
-		for (const struct integ_desc **algp = next_integ_desc(NULL);
-		     algp != NULL; algp = next_integ_desc(algp)) {
-			const struct integ_desc *alg = *algp;
-			if (alg->prf == proposal->prf) {
-				merged_proposal.integ = alg;
-				break;
-			}
-		}
-		if (merged_proposal.integ == NULL) {
-			snprintf(parser->err_buf, parser->err_buf_len,
-				 "%s integrity derived from PRF '%s' is not supported",
-				 proposal->protocol->name,
-				 proposal->prf->common.name);
-			return false;
-		}
-		return add_proposal_defaults(parser, defaults,
-					     alg_info, &merged_proposal);
-	} else {
-		return add_proposal(parser, alg_info, proposal);
-	}
-}
-
-static bool merge_default_proposals(const struct proposal_parser *parser,
-				    struct alg_info *alg_info,
-				    const struct proposal_info *proposal)
-{
-	/*
-	 * If there's a hint of IKEv1 being enabled then prefer its
-	 * larger set of defaults.
-	 *
-	 * This should increase the odds of both ends interoperating.
-	 *
-	 * For instance, the IKEv2 defaults were preferred and one end
-	 * has ikev2=never then, in aggressive mode, things don't
-	 * work.
-	 */
-	const struct proposal_defaults *defaults = (parser->policy->ikev1
-						    ? proposal->protocol->ikev1_defaults
-						    : proposal->protocol->ikev2_defaults);
-	return add_proposal_defaults(parser, defaults,
-				     alg_info, proposal);
-}
-
-static const struct ike_alg *lookup_byname(const struct proposal_parser *parser,
-					   alg_byname_fn *alg_byname,
-					   shunk_t name,
-					   size_t key_bit_length,
-					   shunk_t print_name,
-					   const char *what)
-{
-	if (name.len > 0) {
-		if (alg_byname != NULL) {
-			const struct ike_alg *alg = alg_byname(parser, name, key_bit_length,
-							       print_name);
-			if (alg == NULL) {
-				DBG(DBG_PROPOSAL_PARSER,
-				    DBG_log("%s_byname('"PRI_SHUNK"') failed: %s",
-					    what, PRI_shunk(name),
-					    parser->err_buf));
-				passert(parser->err_buf[0] != '\0');
-				return NULL;
-			}
-			DBG(DBG_PROPOSAL_PARSER,
-			    DBG_log("%s_byname('"PRI_SHUNK"') returned '%s'",
-				    what, PRI_shunk(name), alg->name));
-			return alg;
-		} else {
-			DBG(DBG_PROPOSAL_PARSER,
-			    DBG_log("ignoring %s '"PRI_SHUNK"'",
-				    what, PRI_shunk(name)));
-			return NULL;
-		}
-	}
-	return NULL;
-}
-
-static int parse_eklen(char *err_buf, size_t err_buf_len,
-		       shunk_t buf)
-{
-	/* convert -<eklen> if present */
-	char *end = NULL;
-	long eklen = strtol(buf.ptr, &end, 10);
-	if (buf.ptr + buf.len != end) {
-		snprintf(err_buf, err_buf_len,
-			 "encryption key length '"PRI_SHUNK"' contains a non-numeric character",
-			 PRI_shunk(buf));
-		return 0;
-	}
-	if (eklen >= INT_MAX) {
-		snprintf(err_buf, err_buf_len,
-			 "encryption key length '"PRI_SHUNK"' WAY too big",
-			 PRI_shunk(buf));
-		return 0;
-	}
-	if (eklen == 0) {
-		snprintf(err_buf, err_buf_len,
-			 "encryption key length is zero");
-		return 0;
-	}
-	return eklen;
-}
-
-/*
- * Try to parse any of <ealg>-<ekeylen>, <ealg>_<ekeylen>,
- * <ealg><ekeylen>, or <ealg>.  Strings like aes_gcm_16 and
- * aes_gcm_16_256 end up in alg[0], while strings like aes_gcm_16-256
- * end up in alg[0]-alg[1].
- */
-
-struct token {
-	char sep;
-	shunk_t alg;
-};
-
-static bool parse_encrypt(const struct proposal_parser *parser,
-			  struct token **tokens,
-			  struct proposal_info *proposal)
-{
-	shunk_t ealg = (*tokens)[0].alg;
-	shunk_t eklen = (*tokens)[1].alg;
-	if (eklen.len > 0 && isdigit(eklen.ptr[0])) {
-		/* assume <ealg>-<eklen> */
-		int enckeylen = parse_eklen(parser->err_buf,
-					    parser->err_buf_len,
-					    eklen);
-		if (enckeylen <= 0) {
-			passert(parser->err_buf[0] != '\0');
-			return false;
-		}
-		/* print <alg>-<len> */
-		shunk_t print_name = shunk2(ealg.ptr, eklen.ptr + eklen.len - ealg.ptr);
-		proposal->enckeylen = enckeylen;
-		proposal->encrypt =
-			encrypt_desc(lookup_byname(parser,
-						   encrypt_alg_byname,
-						   ealg, proposal->enckeylen,
-						   print_name, "encryption"));
-		/* Was <ealg>-<eklen> rejected? */
-		if (parser->err_buf[0] != '\0') {
-			return false;
-		}
-		*tokens += 2; /* consume both tokens */
-		return true;
-	}
-	/* try <ealg> */
-	shunk_t print_name = ealg;
-	proposal->encrypt =
-		encrypt_desc(lookup_byname(parser,
-					   encrypt_alg_byname,
-					   ealg, proposal->enckeylen,
-					   print_name, "encryption"));
-	if (parser->err_buf[0] != '\0') {
-		/*
-		 * Could it be <ealg><eklen> or <ealg>_<eklen>?  Work
-		 * backwards skipping any digits.
-		 */
-		shunk_t end = shunk2(ealg.ptr + ealg.len, 0);
-		while (end.ptr > ealg.ptr && isdigit(end.ptr[-1])) {
-			end.ptr--;
-			end.len++;
-		}
-		if (end.len == 0) {
-			/*
-			 * no trailing <eklen> and <ealg> was rejected
-			 */
-			passert(parser->err_buf[0] != '\0');
-			return false;
-		}
-		/* try to convert */
-		int enckeylen = parse_eklen(parser->err_buf, parser->err_buf_len, end);
-		if (enckeylen <= 0) {
-			passert(parser->err_buf[0] != '\0');
-			return false;
-		}
-		proposal->enckeylen = enckeylen;
-		/*
-		 * trim <eklen> from <ealg>; and then trim any
-		 * trailing '_'
-		 */
-		ealg.len = end.ptr - ealg.ptr;
-		if (end.ptr > ealg.ptr && end.ptr[-1] == '_') {
-			ealg.len -= 1;
-		}
-		/* try again */
-		parser->err_buf[0] = '\0';
-		proposal->encrypt =
-			encrypt_desc(lookup_byname(parser,
-						   encrypt_alg_byname,
-						   ealg, proposal->enckeylen,
-						   print_name, "encryption"));
-		if (parser->err_buf[0] != '\0') {
-			return false;
-		}
-	}
-	*tokens += 1; /* consume one token */
-	return true;
-}
-
-static bool parser_alg_info_add(const struct proposal_parser *parser,
-				struct token *tokens, struct proposal_info proposal,
-				struct alg_info *alg_info)
-{
-	LSWDBGP(DBG_PROPOSAL_PARSER, buf) {
-		lswlogs(buf, "algs:");
-		for (struct token *token = tokens; token->alg.ptr != NULL; token++) {
-			lswlogf(buf, " algs[%tu] = '"PRI_SHUNK"'",
-				token - tokens, PRI_shunk(token->alg));
-		}
-	}
-
-	bool lookup_encrypt = parser->protocol->encrypt_alg_byname != NULL;
-	if (!lookup_encrypt && IMPAIR(PROPOSAL_PARSER)) {
-		/* Force lookup, will discard any error. */
-		lookup_encrypt = true;
-	}
-	if (lookup_encrypt && tokens->alg.ptr != NULL && tokens->sep != ';') {
-		if (!parse_encrypt(parser, &tokens, &proposal)) {
-			if (IMPAIR(PROPOSAL_PARSER)) {
-				/* ignore the lookup and stumble on */
-				parser->err_buf[0] = '\0';
-			} else {
-				passert(parser->err_buf[0] != '\0');
-				return false;
-			}
-		}
-	}
-
-	bool lookup_prf = parser->protocol->prf_alg_byname != NULL;
-	if (!lookup_prf && IMPAIR(PROPOSAL_PARSER)) {
-		/*
-		 * Force PRF lookup when the folloing token looks like
-		 * an INTEG algorithm (i.e., its lookup succeeds).
-		 * Otherwise something like ah=sha1 gets parsed as
-		 * ah=[encr]-sha1-[integ]-[dh].
-		 */
-		shunk_t prf = tokens[0].alg;
-		shunk_t integ = tokens[1].alg;
-		if (prf.ptr != NULL && integ.ptr != NULL) {
-			lookup_prf = (lookup_byname(parser, integ_alg_byname,
-						    integ, 0, integ, "integrity")
-				      != NULL);
-			parser->err_buf[0] = '\0';
-		}
-	}
-	if (lookup_prf && tokens->alg.ptr != NULL && tokens->sep != ';') {
-		shunk_t prf = tokens[0].alg;
-		proposal.prf = prf_desc(lookup_byname(parser,
-						      prf_alg_byname,
-						      prf, 0, prf, "PRF"));
-		if (parser->err_buf[0] != '\0') {
-			return false;
-		}
-		tokens += 1; /* consume one arg */
-	}
-
-	bool lookup_integ = parser->protocol->integ_alg_byname != NULL;
-	if (!lookup_integ && IMPAIR(PROPOSAL_PARSER)) {
-		/* force things */
-		lookup_integ = true;
-	}
-	if (lookup_integ && tokens->alg.ptr != NULL && tokens->sep != ';') {
-		shunk_t integ = tokens[0].alg;
-		proposal.integ = integ_desc(lookup_byname(parser,
-							  integ_alg_byname,
-							  integ, 0, integ, "integrity"));
-		if (parser->err_buf[0] != '\0') {
-			if (tokens[1].alg.ptr != NULL) {
-				/*
-				 * This alg should have been
-				 * integrity, since the next would be
-				 * DH; error applies.
-				 */
-				passert(parser->err_buf[0] != '\0');
-				return false;
-			}
-			if (tokens[1].alg.ptr == NULL &&
-			    parser->protocol->prf_alg_byname == NULL) {
-				/*
-				 * Only one arg, integrity is prefered
-				 * to DH (and no PRF); error applies.
-				 */
-				passert(parser->err_buf[0] != '\0');
-				return false;
-			}
-			/* let DH try */
-			parser->err_buf[0] = '\0';
-		} else {
-			tokens += 1; /* consume one arg */
-		}
-	}
-
-	bool lookup_dh = parser->protocol->dh_alg_byname || IMPAIR(PROPOSAL_PARSER);
-	if (lookup_dh && tokens->alg.ptr != NULL) {
-		shunk_t dh = tokens[0].alg;
-		proposal.dh = oakley_group_desc(lookup_byname(parser,
-							      dh_alg_byname,
-							      dh, 0,
-							      dh, "DH"));
-		if (parser->err_buf[0] != '\0') {
-			return false;
-		}
-		tokens += 1; /* consume one arg */
-	}
-
-	if (tokens->alg.ptr != NULL) {
-		snprintf(parser->err_buf, parser->err_buf_len,
-			 "'"PRI_SHUNK"' unexpected",
-			 PRI_shunk(tokens[0].alg));
-		return false;
-	}
-
-	if (IMPAIR(PROPOSAL_PARSER)) {
-		return add_proposal(parser, alg_info, &proposal);
-	} else {
-		return merge_default_proposals(parser, alg_info, &proposal);
-	}
-}
-
-
-bool alg_info_parse_str(const struct proposal_parser *parser,
-			struct alg_info *alg_info,
-			shunk_t alg_str)
-{
-	DBG(DBG_PROPOSAL_PARSER,
-	    DBG_log("parsing '"PRI_SHUNK"' for %s",
-		    PRI_shunk(alg_str), parser->protocol->name));
-
-	/* use default if no string */
-	if (alg_str.ptr == NULL) {
-		const struct proposal_info proposal = {
-			.protocol = parser->protocol,
-		};
-		return merge_default_proposals(parser, alg_info, &proposal);
-	}
-
-	if (alg_str.len == 0) {
-		/* XXX: hack to keep testsuite happy */
-		snprintf(parser->err_buf, parser->err_buf_len,
-			 "String ended with invalid char, just after \"\"");
-		return false;
-	}
-
-	shunk_t prop_ptr = alg_str;
-	do {
-		/* find the next proposal */
-		shunk_t prop = shunk_strsep(&prop_ptr, ",");
-		/* parse it */
-		struct token tokens[8];
-		zero(&tokens);
-		struct token *token = tokens;
-		char last_sep = '\0';
-		shunk_t alg_ptr = prop;
-		do {
-			if (token + 1 >= tokens+elemsof(tokens)) {
-				/* space for NULL? */
-				snprintf(parser->err_buf, parser->err_buf_len,
-					 "proposal too long");
-				return false;
-			}
-			/* find the next alg */
-			shunk_t alg = shunk_strsep(&alg_ptr, "-;,");
-			*token++ = (struct token) {
-				.alg = alg,
-				.sep = last_sep,
-			};
-			last_sep = alg.ptr[alg.len]; /* save separator */
-		} while (alg_ptr.len > 0);
-		struct proposal_info proposal = {
-			.protocol = parser->protocol,
-		};
-		if (!parser_alg_info_add(parser, tokens, proposal,
-					 alg_info)) {
-			passert(parser->err_buf[0] != '\0');
-			return false;
-		}
-	} while (prop_ptr.len > 0);
-	return true;
-}
-
-struct proposal_parser proposal_parser(const struct proposal_policy *policy,
-				       const struct proposal_protocol *protocol,
-				       char *err_buf, size_t err_buf_len)
-{
-	const struct proposal_parser parser = {
-		.policy = policy,
-		.protocol = protocol,
-		.err_buf = err_buf,
-		.err_buf_len = err_buf_len,
-	};
-	err_buf[0] = '\0';
-	return parser;
-}
-
-bool proposal_aead_none_ok(const struct proposal_parser *parser,
-			   const struct proposal_info *proposal)
-{
-	if (IMPAIR(ALLOW_NULL_NONE)) {
-		return true;
-	}
-
-	if (proposal->encrypt != NULL &&
-	    encrypt_desc_is_aead(proposal->encrypt) &&
-	    proposal->integ != NULL &&
-	    proposal->integ != &ike_alg_integ_none) {
-		/*
-		 * For instance, esp=aes_gcm-sha1" is invalid.
-		 */
-		snprintf(parser->err_buf, parser->err_buf_len,
-			 "AEAD %s encryption algorithm '%s' must have 'none' as the integrity algorithm",
-			 proposal->protocol->name,
-			 proposal->encrypt->common.name);
-		return false;
-	}
-
-	if (proposal->encrypt != NULL &&
-	    !encrypt_desc_is_aead(proposal->encrypt) &&
-	    proposal->integ != NULL &&
-	    proposal->integ == &ike_alg_integ_none) {
-		/*
-		 * For instance, esp=aes_cbc-none" is invalid.
-		 */
-		snprintf(parser->err_buf, parser->err_buf_len,
-			 "non-AEAD %s encryption algorithm '%s' cannot have 'none' as the integrity algorithm",
-			 proposal->protocol->name,
-			 proposal->encrypt->common.name);
-		return false;
-	}
-
-	return true;
-}
-
-/*
- * alg_info struct can be shared by several connections instances,
- * handle free() with ref_cnts.
- *
- * Use alg_info_free() if the value returned by *_parse_str() is found
- * to be (semantically) bogus.
- */
-
-void alg_info_free(struct alg_info *alg_info)
-{
-	passert(alg_info);
-	passert(alg_info->ref_cnt == 0);
-	pfree(alg_info);
-}
-
-void alg_info_addref(struct alg_info *alg_info)
-{
-	alg_info->ref_cnt++;
-}
-
-void alg_info_delref(struct alg_info *alg_info)
-{
-	passert(alg_info->ref_cnt != 0);
-	alg_info->ref_cnt--;
-	if (alg_info->ref_cnt == 0)
-		alg_info_free(alg_info);
-}
-
-size_t lswlog_proposal_info(struct lswlog *log,
-			    const struct proposal_info *proposal)
-{
- 	size_t size = 0;
- 	const char *sep = "";
-
-	if (proposal->encrypt != NULL) {
-		size += lswlogs(log, sep); sep = "-";
-		size += lswlogs(log, proposal->encrypt->common.fqn);
-		if (proposal->enckeylen != 0) {
-			size += lswlogf(log, "_%zd", proposal->enckeylen);
-		}
-	} else if (IMPAIR(PROPOSAL_PARSER)) {
-		size += lswlogs(log, sep); sep = "-";
-		size += lswlogs(log, "[ENCRYPT]");
-	}
-
-	if (proposal->prf != NULL) {
-		size += lswlogs(log, sep); sep = "-";
-		size += lswlogs(log, proposal->prf->common.fqn);
-	} else if (IMPAIR(PROPOSAL_PARSER)) {
-		size += lswlogs(log, sep); sep = "-";
-		size += lswlogs(log, "[PRF]");
-	}
-
-	if (proposal->integ != NULL && proposal->prf == NULL) {
-		size += lswlogs(log, sep); sep = "-";
-		size += lswlogs(log, proposal->integ->common.fqn);
-	} else if (!(proposal->integ == &ike_alg_integ_none &&
-		     encrypt_desc_is_aead(proposal->encrypt)) &&
-		   proposal->integ != NULL && proposal->integ->prf != proposal->prf) {
-		size += lswlogs(log, sep); sep = "-";
-		size += lswlogs(log, proposal->integ->common.fqn);
-	} else if (IMPAIR(PROPOSAL_PARSER)) {
-		size += lswlogs(log, sep); sep = "-";
-		if (proposal->integ != NULL) {
-			size += lswlogs(log, proposal->integ->common.fqn);
-		} else {
-			size += lswlogs(log, "[INTEG]");
-		}
-	}
-
-	if (proposal->dh != NULL) {
-		size += lswlogs(log, sep); sep = "-";	/* sep not subsequently used */
-		size += lswlogs(log, proposal->dh->common.fqn);
-	} else if (IMPAIR(PROPOSAL_PARSER)) {
-		size += lswlogs(log, sep); sep = "-";	/* sep not subsequently used */
-		size += lswlogs(log, "[DH]");
-	}
-
-	return size;
-}
-
-size_t lswlog_alg_info(struct lswlog *log, const struct alg_info *alg_info)
-{
-	size_t size = 0;
-	const char *sep = "";
-	FOR_EACH_PROPOSAL_INFO(alg_info, proposal) {
-		size += lswlogs(log, sep);
-		size += lswlog_proposal_info(log, proposal);
-		sep = ", ";
-	}
-	return size;
-}
-
-/*
- * When PFS=no ignore any DH algorithms, and when PFS=yes reject
- * mixing implict and explicit DH.
- */
-bool alg_info_pfs_vs_dh_check(const struct proposal_parser *parser,
-			      struct alg_info_esp *aie)
-{
-	if (aie->ai.alg_info_cnt <= 0) {
-		/* let caller deal with no proposals. */
-		return true;
-	}
-
-	/* scrape the proposals for dh algorithms */
-	struct proposal_info *first_null = NULL;
-	struct proposal_info *first_dh = NULL;
-	struct proposal_info *second_dh = NULL;
-	struct proposal_info *first_none = NULL;
-	FOR_EACH_ESP_INFO(aie, alg) {
-		if (alg->dh == NULL) {
-			if (first_null == NULL) {
-				first_null = alg;
-			}
-		} else if (alg->dh == &ike_alg_dh_none) {
-			if (first_none == NULL) {
-				first_none = alg;
-			}
-		} else if (first_dh == NULL) {
-			first_dh = alg;
-		} else if (second_dh == NULL && first_dh->dh != alg->dh) {
-			second_dh = alg;
-		}
-	}
-
-	if (first_dh == NULL && first_none == NULL) {
-		/* no DH is always ok */
-		return true;
-	}
-
-	/*
-	 * Try to generate very specific errors first.  For instance,
-	 * given PFS=no esp=aes,aes;dh21, an error stating that dh21
-	 * is not valid because of PFS is more helpful than an error
-	 * saying that all or no proposals need PFS.
-	 */
-
-	/*
-	 * Since PFS=NO overrides any DH, don't silently ignore it.
-	 * Check this early so that a conflict with PFS=no code gets
-	 * reported before anything else.
-	 */
-	if (!parser->policy->pfs && (first_dh != NULL || first_none != NULL)) {
-		FOR_EACH_ESP_INFO(aie, alg) {
-			if (alg->dh == &ike_alg_dh_none) {
-				parser->policy->warning("ignoring redundant %s DH algorithm NONE as PFS policy is disabled",
-							parser->protocol->name);
-			} else if (alg->dh != NULL) {
-				parser->policy->warning("ignoring %s DH algorithm %s as PFS policy is disabled",
-							parser->protocol->name,
-							alg->dh->common.fqn);
-			}
-			alg->dh = NULL;
-		}
-		return true;
-	}
-
-	/*
-	 * Since at least one proposal included DH, all proposals
-	 * should.  A proposal without DH is an error.
-	 *
-	 * (The converse, no proposals including DH was handled right
-	 * at the start).
-	 */
-	if (first_null != NULL) {
-		/* DH was specified */
-		snprintf(parser->err_buf, parser->err_buf_len,
-			 "either all or no %s proposals should specify DH",
-			 parser->protocol->name);
-		if (!impair_proposal_errors(parser)) {
-			return false;
-		}
-	}
-
-	/*
-	 * IKEv1 only allows one DH algorithm.
-	 */
-	if (parser->policy->ikev1) {
-		if (first_dh != NULL && second_dh != NULL) {
-			snprintf(parser->err_buf, parser->err_buf_len,
-				 "more than one IKEv1 %s DH algorithm (%s, %s) is not allowed in quick mode",
-				 parser->protocol->name,
-				 first_dh->dh->common.fqn,
-				 second_dh->dh->common.fqn);
-			if (!impair_proposal_errors(parser)) {
-				return false;
-			}
-		}
-	}
-
-	/*
-	 * IKEv2, only implements one DH algorithm.
-	 */
-	if (parser->policy->ikev2) {
-		if (first_dh != NULL && second_dh != NULL) {
-			snprintf(parser->err_buf, parser->err_buf_len,
-				 "more than one IKEv2 %s DH algorithm (%s, %s) requires unimplemented CHILD_SA INVALID_KE",
-				 parser->protocol->name,
-				 first_dh->dh->common.fqn,
-				 second_dh->dh->common.fqn);
-			if (!impair_proposal_errors(parser)) {
-				return false;
-			}
-		}
-	}
-
-	return true;
-}
-
-bool impair_proposal_errors(const struct proposal_parser *parser)
-{
-	pexpect(parser->err_buf[0] != '\0');
-	if (IMPAIR(PROPOSAL_PARSER)) {
-		libreswan_log("IMPAIR: ignoring proposal error: %s",
-			      parser->err_buf);
-		parser->err_buf[0] = '\0';
-		return true;
-	} else {
-		return false;
-	}
-}
diff -Naur libreswan-3.27-orig/lib/libswan/esp_info.c libreswan-3.27/lib/libswan/esp_info.c
--- libreswan-3.27-orig/lib/libswan/esp_info.c	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/lib/libswan/esp_info.c	2019-02-15 16:50:12.776728076 -0500
@@ -22,7 +22,7 @@
 
 #include "lswalloc.h"
 #include "lswlog.h"
-#include "alg_info.h"
+#include "proposals.h"
 #include "alg_byname.h"
 #include "lswfips.h"
 
@@ -33,8 +33,8 @@
 /*
  * Add ESP alg info _with_ logic (policy):
  */
-static bool esp_proposal_ok(const struct proposal_parser *parser,
-			    const struct proposal_info *proposal)
+static bool esp_proposal_ok(struct proposal_parser *parser,
+			    const struct proposal *proposal)
 {
 	if (!proposal_aead_none_ok(parser, proposal)) {
 		if (!impair_proposal_errors(parser)) {
@@ -42,12 +42,19 @@
 		}
 	}
 
-	impaired_passert(PROPOSAL_PARSER, proposal->encrypt != NULL);
-	impaired_passert(PROPOSAL_PARSER, proposal->prf == NULL);
-	impaired_passert(PROPOSAL_PARSER, proposal->integ != NULL);
+	impaired_passert(PROPOSAL_PARSER,
+			 next_algorithm(proposal, PROPOSAL_encrypt, NULL) != NULL);
+	impaired_passert(PROPOSAL_PARSER,
+			 next_algorithm(proposal, PROPOSAL_prf, NULL) == NULL);
+	impaired_passert(PROPOSAL_PARSER,
+			 next_algorithm(proposal, PROPOSAL_integ, NULL) != NULL);
 	return true;
 }
 
+/*
+ * since esp= must have an encryption algorithm this is normally
+ * ignored.
+ */
 static const struct ike_alg *default_esp_encrypt[] = {
 #ifdef USE_AES
 	&ike_alg_encrypt_aes_cbc.common,
@@ -55,24 +62,39 @@
 	NULL,
 };
 
-static const struct ike_alg *default_esp_integ[] = {
+static const struct ike_alg *default_v1_esp_integ[] = {
 #ifdef USE_SHA1
 	&ike_alg_integ_sha1.common,
 #endif
 	NULL,
 };
 
-static const struct proposal_defaults esp_defaults = {
+static const struct ike_alg *default_v2_esp_integ[] = {
+#ifdef USE_SHA2
+	&ike_alg_integ_sha2_512.common,
+	&ike_alg_integ_sha2_256.common,
+#endif
+	NULL,
+};
+
+static const struct proposal_defaults v1_esp_defaults = {
 	.encrypt = default_esp_encrypt,
-	.integ = default_esp_integ,
+	.integ = default_v1_esp_integ,
+};
+
+static const struct proposal_defaults v2_esp_defaults = {
+	.encrypt = default_esp_encrypt,
+	.integ = default_v2_esp_integ,
 };
 
 static const struct proposal_protocol esp_proposal_protocol = {
 	.name = "ESP",
 	.ikev1_alg_id = IKEv1_ESP_ID,
 	.protoid = PROTO_IPSEC_ESP,
-	.ikev1_defaults = &esp_defaults,
-	.ikev2_defaults = &esp_defaults,
+	.defaults = {
+		[IKEv1] = &v1_esp_defaults,
+		[IKEv2] = &v2_esp_defaults,
+	},
 	.proposal_ok = esp_proposal_ok,
 	.encrypt_alg_byname = encrypt_alg_byname,
 	.integ_alg_byname = integ_alg_byname,
@@ -81,7 +103,7 @@
 
 /*
  * ??? the only difference between
- * alg_info_ah_create_from_str and alg_info_esp_create_from_str
+ * alg_info_ah_create_from_str and esp_proposals_create_from_str
  * is in the second argument to proposal_parser.
  *
  * XXX: On the other hand, since "struct ike_info" and "struct
@@ -91,33 +113,7 @@
 
 /* This function is tested in testing/algparse/algparse.c */
 
-struct alg_info_esp *alg_info_esp_create_from_str(const struct proposal_policy *policy,
-						  const char *alg_str,
-						  char *err_buf, size_t err_buf_len)
+struct proposal_parser *esp_proposal_parser(const struct proposal_policy *policy)
 {
-	shunk_t string = shunk1(alg_str);
-	const struct proposal_parser parser = proposal_parser(policy,
-							      &esp_proposal_protocol,
-							      err_buf, err_buf_len);
-
-	/*
-	 * alg_info storage should be sized dynamically
-	 * but this may require two passes to know
-	 * transform count in advance.
-	 */
-	struct alg_info_esp *alg_info_esp = alloc_thing(struct alg_info_esp,
-							"alg_info_esp");
-	if (!alg_info_parse_str(&parser, &alg_info_esp->ai, string)) {
-		passert(err_buf[0] != '\0');
-		alg_info_free(&alg_info_esp->ai);
-		return NULL;
-	}
-
-	if (!alg_info_pfs_vs_dh_check(&parser, alg_info_esp)) {
-		passert(err_buf[0] != '\0');
-		alg_info_free(&alg_info_esp->ai);
-		return NULL;
-	}
-
-	return alg_info_esp;
+	return alloc_proposal_parser(policy, &esp_proposal_protocol);
 }
diff -Naur libreswan-3.27-orig/lib/libswan/ike_alg.c libreswan-3.27/lib/libswan/ike_alg.c
--- libreswan-3.27-orig/lib/libswan/ike_alg.c	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/lib/libswan/ike_alg.c	2019-02-15 16:32:28.978835332 -0500
@@ -37,7 +37,7 @@
 #include "ike_alg.h"
 #include "ike_alg_integ.h"
 #include "ike_alg_encrypt.h"
-#include "alg_info.h"
+#include "proposals.h"
 #include "ike_alg_prf.h"
 #include "ike_alg_prf_hmac_ops.h"
 #include "ike_alg_prf_nss_ops.h"
@@ -589,7 +589,7 @@
 static bool integ_desc_is_ike(const struct ike_alg *alg)
 {
 	const struct integ_desc *integ = integ_desc(alg);
-	return integ->prf != NULL;
+	return integ->prf != NULL || integ == &ike_alg_integ_none;
 }
 
 static struct algorithm_table integ_algorithms = ALGORITHM_TABLE(integ_descriptors);
diff -Naur libreswan-3.27-orig/lib/libswan/ike_alg_dh.c libreswan-3.27/lib/libswan/ike_alg_dh.c
--- libreswan-3.27-orig/lib/libswan/ike_alg_dh.c	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/lib/libswan/ike_alg_dh.c	2019-02-15 16:32:28.979835342 -0500
@@ -186,7 +186,7 @@
 		.algo_type = IKE_ALG_DH,
 		.name = "DH19",
 		.fqn = "DH19",
-		.names = { "dh19", "ecp_256", },
+		.names = { "dh19", "ecp_256", "ecp256" },
 		.id = {
 			[IKEv1_OAKLEY_ID] = OAKLEY_GROUP_ECP_256,
 			[IKEv1_ESP_ID] = -1,
@@ -205,7 +205,7 @@
 		.algo_type = IKE_ALG_DH,
 		.name = "DH20",
 		.fqn = "DH20",
-		.names = { "dh20", "ecp_384", },
+		.names = { "dh20", "ecp_384", "ecp384" },
 		.id = {
 			[IKEv1_OAKLEY_ID] = OAKLEY_GROUP_ECP_384,
 			[IKEv1_ESP_ID] = -1,
@@ -224,7 +224,7 @@
 		.algo_type = IKE_ALG_DH,
 		.name = "DH21",
 		.fqn = "DH21",
-		.names = { "dh21", "ecp_521", },
+		.names = { "dh21", "ecp_521", "ecp521" },
 		.id = {
 			[IKEv1_OAKLEY_ID] = OAKLEY_GROUP_ECP_521,
 			[IKEv1_ESP_ID] = -1,
diff -Naur libreswan-3.27-orig/lib/libswan/ike_alg_sha2.c libreswan-3.27/lib/libswan/ike_alg_sha2.c
--- libreswan-3.27-orig/lib/libswan/ike_alg_sha2.c	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/lib/libswan/ike_alg_sha2.c	2019-02-15 16:32:28.979835342 -0500
@@ -79,7 +79,7 @@
 	.common = {
 		.name = "sha2_256",
 		.fqn = "HMAC_SHA2_256_128",
-		.names = { "sha2", "sha256", "sha2_256", "hmac_sha2_256", "hmac_sha2_256_128", },
+		.names = { "sha2", "sha256", "sha2_256", "sha2_256_128", "hmac_sha2_256", "hmac_sha2_256_128", },
 		.algo_type = IKE_ALG_INTEG,
 		.id = {
 			[IKEv1_OAKLEY_ID] = OAKLEY_SHA2_256,
@@ -206,7 +206,7 @@
 	.common = {
 		.name = "sha2_384",
 		.fqn = "HMAC_SHA2_384_192",
-		.names = { "sha384", "sha2_384", "hmac_sha2_384", "hmac_sha2_384_192", },
+		.names = { "sha384", "sha2_384", "sha2_384_192", "hmac_sha2_384", "hmac_sha2_384_192", },
 		.algo_type = IKE_ALG_INTEG,
 		.id = {
 			[IKEv1_OAKLEY_ID] = OAKLEY_SHA2_384,
@@ -308,7 +308,7 @@
 	.common = {
 		.name = "sha2_512",
 		.fqn = "HMAC_SHA2_512_256",
-		.names = { "sha512", "sha2_512", "hmac_sha2_512", "hmac_sha2_512_256", },
+		.names = { "sha512", "sha2_512", "sha2_512_256", "hmac_sha2_512", "hmac_sha2_512_256", },
 		.algo_type = IKE_ALG_INTEG,
 		.id = {
 			[IKEv1_OAKLEY_ID] = OAKLEY_SHA2_512,
diff -Naur libreswan-3.27-orig/lib/libswan/ike_info.c libreswan-3.27/lib/libswan/ike_info.c
--- libreswan-3.27-orig/lib/libswan/ike_info.c	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/lib/libswan/ike_info.c	2019-02-15 16:50:12.777728085 -0500
@@ -27,10 +27,10 @@
 #include "ike_alg_integ.h"
 #include "ike_alg_prf.h"
 #include "ike_alg_dh.h"
-#include "alg_info.h"
+#include "proposals.h"
 
-static bool ike_proposal_ok(const struct proposal_parser *parser,
-			    const struct proposal_info *proposal)
+static bool ike_proposal_ok(struct proposal_parser *parser,
+			    const struct proposal *proposal)
 {
 	if (!proposal_aead_none_ok(parser, proposal)) {
 		if (!impair_proposal_errors(parser)) {
@@ -42,27 +42,42 @@
 	 * Check that the ALG_INFO spec is implemented.
 	 */
 
-	impaired_passert(PROPOSAL_PARSER, proposal->encrypt != NULL);
-	passert(proposal->encrypt == NULL || ike_alg_is_ike(&(proposal->encrypt->common)));
-	passert(IMPAIR(PROPOSAL_PARSER) || proposal->enckeylen == 0 ||
-		encrypt_has_key_bit_length(proposal->encrypt,
-					   proposal->enckeylen));
-
-	impaired_passert(PROPOSAL_PARSER, proposal->prf != NULL);
-	passert(proposal->prf == NULL || ike_alg_is_ike(&(proposal->prf->common)));
-
-	impaired_passert(PROPOSAL_PARSER, proposal->integ != NULL);
-	passert(proposal->integ == &ike_alg_integ_none ||
-		proposal->integ == NULL ||
-		ike_alg_is_ike(&proposal->integ->common));
-
-	impaired_passert(PROPOSAL_PARSER, proposal->dh != NULL);
-	passert(proposal->dh == NULL || ike_alg_is_ike(&(proposal->dh->common)));
-	if (proposal->dh == &ike_alg_dh_none) {
-		snprintf(parser->err_buf, parser->err_buf_len,
-			 "IKE DH algorithm 'none' not permitted");
-		if (!impair_proposal_errors(parser)) {
-			return false;
+	impaired_passert(PROPOSAL_PARSER,
+			next_algorithm(proposal, PROPOSAL_encrypt, NULL) != NULL);
+	FOR_EACH_ALGORITHM(proposal, encrypt, alg) {
+		const struct encrypt_desc *encrypt = encrypt_desc(alg->desc);
+		passert(ike_alg_is_ike(&encrypt->common));
+		passert(IMPAIR(PROPOSAL_PARSER) ||
+			alg->enckeylen == 0 ||
+			encrypt_has_key_bit_length(encrypt,
+				alg->enckeylen));
+	}
+
+	impaired_passert(PROPOSAL_PARSER,
+			next_algorithm(proposal, PROPOSAL_prf, NULL) != NULL);
+	FOR_EACH_ALGORITHM(proposal, prf, alg) {
+		const struct prf_desc *prf = prf_desc(alg->desc);
+		passert(ike_alg_is_ike(&prf->common));
+	}
+
+	impaired_passert(PROPOSAL_PARSER,
+			next_algorithm(proposal, PROPOSAL_integ, NULL) != NULL);
+	FOR_EACH_ALGORITHM(proposal, integ, alg) {
+		const struct integ_desc *integ = integ_desc(alg->desc);
+		passert(integ == &ike_alg_integ_none ||
+			ike_alg_is_ike(&integ->common));
+	}
+
+	impaired_passert(PROPOSAL_PARSER,
+			next_algorithm(proposal, PROPOSAL_dh, NULL) != NULL);
+	FOR_EACH_ALGORITHM(proposal, dh, alg) {
+		const struct oakley_group_desc *dh = dh_desc(alg->desc);
+		passert(ike_alg_is_ike(&dh->common));
+		if (dh == &ike_alg_dh_none) {
+			proposal_error(parser, "IKE DH algorithm 'none' not permitted");
+			if (!impair_proposal_errors(parser)) {
+				return false;
+			}
 		}
 	}
 
@@ -84,9 +99,20 @@
 };
 static const struct ike_alg *default_ikev2_groups[] = {
 	&oakley_group_modp2048.common,
+	&oakley_group_modp3072.common,
+	&oakley_group_modp4096.common,
+	&oakley_group_modp8192.common,
+	&oakley_group_dh19.common,
+	&oakley_group_dh20.common,
+	&oakley_group_dh21.common,
+	&oakley_group_dh31.common,
 	NULL,
 };
 
+/*
+ * since ike= must have an encryption algorithm this is normally
+ * ignored.
+ */
 static const struct ike_alg *default_ike_ealgs[] = {
 #ifdef USE_AES
 	&ike_alg_encrypt_aes_cbc.common,
@@ -97,7 +123,7 @@
 	NULL,
 };
 
-static const struct ike_alg *default_ike_aalgs[] = {
+static const struct ike_alg *default_v1_ike_prfs[] = {
 #ifdef USE_SHA2
 	&ike_alg_prf_sha2_256.common,
 	&ike_alg_prf_sha2_512.common,
@@ -108,24 +134,34 @@
 	NULL,
 };
 
+static const struct ike_alg *default_v2_ike_prfs[] = {
+#ifdef USE_SHA2
+	&ike_alg_prf_sha2_512.common,
+	&ike_alg_prf_sha2_256.common,
+#endif
+	NULL,
+};
+
 const struct proposal_defaults ikev1_ike_defaults = {
 	.dh = default_ikev1_groups,
 	.encrypt = default_ike_ealgs,
-	.prf = default_ike_aalgs,
+	.prf = default_v1_ike_prfs,
 };
 
 const struct proposal_defaults ikev2_ike_defaults = {
 	.dh = default_ikev2_groups,
 	.encrypt = default_ike_ealgs,
-	.prf = default_ike_aalgs,
+	.prf = default_v2_ike_prfs,
 };
 
 const struct proposal_protocol ike_proposal_protocol = {
 	.name = "IKE",
 	.ikev1_alg_id = IKEv1_OAKLEY_ID,
 	.protoid = PROTO_ISAKMP,
-	.ikev1_defaults = &ikev1_ike_defaults,
-	.ikev2_defaults = &ikev2_ike_defaults,
+	.defaults = {
+		[IKEv1] = &ikev1_ike_defaults,
+		[IKEv2] = &ikev2_ike_defaults,
+	},
 	.proposal_ok = ike_proposal_ok,
 	.encrypt_alg_byname = encrypt_alg_byname,
 	.prf_alg_byname = prf_alg_byname,
@@ -133,25 +169,7 @@
 	.dh_alg_byname = dh_alg_byname,
 };
 
-struct alg_info_ike *alg_info_ike_create_from_str(const struct proposal_policy *policy,
-						  const char *alg_str,
-						  char *err_buf, size_t err_buf_len)
+struct proposal_parser *ike_proposal_parser(const struct proposal_policy *policy)
 {
-	/*
-	 *      alg_info storage should be sized dynamically
-	 *      but this may require two passes to know
-	 *      transform count in advance.
-	 */
-	struct alg_info_ike *alg_info_ike = alloc_thing(struct alg_info_ike, "alg_info_ike");
-	const struct proposal_parser parser = proposal_parser(policy,
-							      &ike_proposal_protocol,
-							      err_buf, err_buf_len);
-
-	if (!alg_info_parse_str(&parser, &alg_info_ike->ai, shunk1(alg_str))) {
-		passert(err_buf[0] != '\0');
-		alg_info_free(&alg_info_ike->ai);
-		return NULL;
-	}
-
-	return alg_info_ike;
+	return alloc_proposal_parser(policy, &ike_proposal_protocol);
 }
diff -Naur libreswan-3.27-orig/lib/libswan/Makefile libreswan-3.27/lib/libswan/Makefile
--- libreswan-3.27-orig/lib/libswan/Makefile	2019-02-15 16:31:43.029408030 -0500
+++ libreswan-3.27/lib/libswan/Makefile	2019-02-15 16:32:28.981835360 -0500
@@ -74,7 +74,12 @@
 OBJS += satot.o
 OBJS += ultot.o
 
-OBJS += alg_info.o esp_info.o ike_info.o ah_info.o
+OBJS += proposals.o
+OBJS += v1_proposals.o
+OBJS += v2_proposals.o
+OBJS += esp_info.o
+OBJS += ah_info.o
+OBJS += ike_info.o
 
 OBJS += ckaid.o
 
diff -Naur libreswan-3.27-orig/lib/libswan/proposals.c libreswan-3.27/lib/libswan/proposals.c
--- libreswan-3.27-orig/lib/libswan/proposals.c	1969-12-31 19:00:00.000000000 -0500
+++ libreswan-3.27/lib/libswan/proposals.c	2019-02-15 16:46:01.378390204 -0500
@@ -0,0 +1,573 @@
+/*
+ * Algorithm info parsing and creation functions
+ * Author: JuanJo Ciarlante <jjo-ipsec@mendoza.gov.ar>
+ *
+ * Copyright (C) 2012 Paul Wouters <paul@libreswan.org>
+ * Copyright (C) 2015-2019 Andrew Cagney
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <https://www.gnu.org/licenses/gpl2.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+
+#include "lswlog.h"
+#include "lswalloc.h"
+#include "constants.h"
+#include "proposals.h"
+#include "ike_alg.h"
+#include "ike_alg_integ.h"
+#include "ike_alg_dh.h"
+#include "alg_byname.h"
+
+struct proposal {
+	/*
+	 * The algorithm entries.
+	 */
+	struct algorithm *algorithms[PROPOSAL_ALGORITHM_ROOF];
+	/*
+	 * Which protocol is this proposal intended for?
+	 */
+	const struct proposal_protocol *protocol;
+	struct proposal *next;
+};
+
+struct proposals {
+	int ref_cnt;
+	struct proposal *proposals;
+};
+
+struct proposal_parser *alloc_proposal_parser(const struct proposal_policy *policy,
+					      const struct proposal_protocol *protocol)
+{
+	struct proposal_parser *parser = alloc_thing(struct proposal_parser, "parser");
+	parser->policy = policy;
+	parser->protocol = protocol;
+	parser->error[0] = '\0';
+	return parser;
+}
+
+void free_proposal_parser(struct proposal_parser **parser)
+{
+	pfree(*parser);
+	*parser = NULL;
+}
+
+bool proposal_encrypt_aead(const struct proposal *proposal)
+{
+	if (proposal->algorithms[PROPOSAL_encrypt] == NULL) {
+		return false;
+	}
+	FOR_EACH_ALGORITHM(proposal, encrypt, alg) {
+		const struct encrypt_desc *encrypt = encrypt_desc(alg->desc);
+		if (!encrypt_desc_is_aead(encrypt)) {
+			return false;
+		}
+	}
+	return true;
+}
+
+bool proposal_encrypt_norm(const struct proposal *proposal)
+{
+	if (proposal->algorithms[PROPOSAL_encrypt] == NULL) {
+		return false;
+	}
+	FOR_EACH_ALGORITHM(proposal, encrypt, alg) {
+		const struct encrypt_desc *encrypt = encrypt_desc(alg->desc);
+		if (encrypt_desc_is_aead(encrypt)) {
+			return false;
+		}
+	}
+	return true;
+}
+
+bool proposal_integ_none(const struct proposal *proposal)
+{
+	/* interpret NULL as NONE */
+	FOR_EACH_ALGORITHM(proposal, integ, alg) {
+		const struct integ_desc *integ = integ_desc(alg->desc);
+		if (integ != &ike_alg_integ_none) {
+			return false;
+		}
+	}
+	return true;
+}
+
+bool proposal_aead_none_ok(struct proposal_parser *parser,
+			   const struct proposal *proposal)
+{
+	if (IMPAIR(ALLOW_NULL_NONE)) {
+		return true;
+	}
+
+	if (proposal->algorithms[PROPOSAL_encrypt] == NULL) {
+		return true;
+	}
+
+	/* are any and all encrypt algorithms AEAD? */
+	bool aead = proposal_encrypt_aead(proposal);
+	bool norm = proposal_encrypt_norm(proposal);
+
+	if (!aead && !norm) {
+		proposal_error(parser, "AEAD and non-AEAD %s encryption algorithm can not be combined",
+			       proposal->protocol->name);
+		return false;
+	}
+
+	/* are any and all integ algorithms NONE? */
+	bool none = proposal_integ_none(proposal);
+
+	if (aead && !none) {
+		const struct ike_alg *encrypt = proposal->algorithms[PROPOSAL_encrypt]->desc;
+		/*
+		 * At least one of the integrity algorithms wasn't
+		 * NONE.  For instance, esp=aes_gcm-sha1" is invalid.
+		 */
+		proposal_error(parser, "AEAD %s encryption algorithm '%s' must have 'none' as the integrity algorithm",
+			       proposal->protocol->name,
+			       encrypt->name);
+		return false;
+	}
+
+	if (norm && none) {
+		const struct ike_alg *encrypt = proposal->algorithms[PROPOSAL_encrypt]->desc;
+		/*
+		 * Not AEAD and either there was no integrity
+		 * algorithm (implying NONE) or at least one integrity
+		 * algorithm was NONE.  For instance,
+		 * esp=aes_cbc-none" is invalid.
+		 */
+		proposal_error(parser, "non-AEAD %s encryption algorithm '%s' cannot have 'none' as the integrity algorithm",
+			       proposal->protocol->name,
+			       encrypt->name);
+		return false;
+	}
+
+	return true;
+}
+
+
+/*
+ * proposals struct can be shared by several connections instances,
+ * handle free() with ref_cnts.
+ */
+
+void proposals_addref(struct proposals **proposals)
+{
+	if ((*proposals) != NULL) {
+		(*proposals)->ref_cnt++;
+	}
+}
+
+void proposals_delref(struct proposals **proposals)
+{
+	if ((*proposals) != NULL) {
+		if ((*proposals)->ref_cnt == 0) {
+			free_proposal(&(*proposals)->proposals);
+			pfree((*proposals));
+		} else {
+			(*proposals)->ref_cnt--;
+		}
+		*proposals = NULL;
+	}
+}
+struct proposal *next_proposal(const struct proposals *proposals,
+			       struct proposal *last)
+{
+	if (last == NULL) {
+		return proposals->proposals;
+	} else {
+		return last->next;
+	}
+}
+
+unsigned nr_proposals(struct proposals *proposals)
+{
+	unsigned nr = 0;
+	FOR_EACH_PROPOSAL(proposals, proposal) {
+		nr++;
+	}
+	return nr;
+}
+
+void append_proposal(struct proposals *proposals, struct proposal **proposal)
+{
+	struct proposal **end = &proposals->proposals;
+	/* check for duplicates */
+	while ((*end) != NULL) {
+		bool same = true;
+		for (enum proposal_algorithm pa = 0;
+		     same && pa < PROPOSAL_ALGORITHM_ROOF; pa++) {
+			struct algorithm *old = (*end)->algorithms[pa];
+			struct algorithm *new = (*proposal)->algorithms[pa];
+			while (same) {
+				if (new == NULL && old == NULL) {
+					break;
+				}
+				if (new == NULL || old == NULL) {
+					same = false;
+					break;
+				}
+				if (new->desc != old->desc) {
+					same = false;
+					break;
+				}
+				/*
+				 * If list already contains encryption
+				 * with ENCKEYLEN=0 then new is a
+				 * duplicate as 0 generates all keys.
+				 * Ignore reverse vis aes128,aes.
+				 */
+				if (old->desc->algo_type == IKE_ALG_ENCRYPT &&
+				    (old->enckeylen != 0 &&
+				     new->enckeylen != old->enckeylen)) {
+					same = false;
+					break;
+				}
+				new = new->next;
+				old = old->next;
+			}
+		}
+		if (same) {
+			/* parser->policy->warning("discarding duplicate proposal"); */
+			free_proposal(proposal);
+			return;
+		}
+		end = &(*end)->next;
+	}
+	*end = *proposal;
+	*proposal = NULL;
+}
+
+struct v1_proposal v1_proposal(const struct proposal *proposal)
+{
+	struct v1_proposal v1 = {
+		.protocol = proposal->protocol,
+#define D(ALG) .ALG = proposal->algorithms[PROPOSAL_##ALG] != NULL ? ALG##_desc(proposal->algorithms[PROPOSAL_##ALG]->desc) : NULL
+		D(encrypt),
+		D(prf),
+		D(integ),
+		D(dh),
+#undef D
+	};
+	v1.enckeylen = proposal->algorithms[PROPOSAL_encrypt] != NULL ? proposal->algorithms[PROPOSAL_encrypt]->enckeylen : 0;
+
+	return v1;
+}
+
+struct algorithm *next_algorithm(const struct proposal *proposal,
+				 enum proposal_algorithm algorithm,
+				 struct algorithm *last)
+{
+	if (last == NULL) {
+		/*
+		 * Hack, there should there a way to index algorithm
+		 * types; however the old enum proved very dangerous.
+		 */
+		passert(algorithm < elemsof(proposal->algorithms));
+		return proposal->algorithms[algorithm];
+	} else {
+		return last->next;
+	}
+}
+
+void free_algorithms(struct proposal *proposal, 
+		     enum proposal_algorithm algorithm)
+{
+	passert(algorithm < elemsof(proposal->algorithms));
+	struct algorithm *alg = proposal->algorithms[algorithm];
+	while (alg != NULL) {
+		struct algorithm *del = alg;
+		alg = alg->next;
+		pfree(del);
+	}
+	proposal->algorithms[algorithm] = NULL;
+}
+
+struct proposal *alloc_proposal(struct proposal_parser *parser)
+{
+	struct proposal *proposal = alloc_thing(struct proposal, "proposal");
+	proposal->protocol = parser->protocol;
+	return proposal;
+}
+
+void free_proposal(struct proposal **proposals)
+{
+	struct proposal *proposal = *proposals;
+	while (proposal != NULL) {
+		struct proposal *del = proposal;
+		proposal = proposal->next;
+		for (enum proposal_algorithm algorithm = 0;
+		     algorithm < PROPOSAL_ALGORITHM_ROOF;
+		     algorithm++) {
+			free_algorithms(del, algorithm);
+		}
+		pfree(del);
+	}
+	*proposals = NULL;
+}
+
+void append_algorithm(struct proposal_parser *parser,
+		      struct proposal *proposal,
+		      enum proposal_algorithm algorithm,
+		      const struct ike_alg *alg,
+		      int enckeylen)
+{
+	passert(algorithm < elemsof(proposal->algorithms));
+	struct algorithm **end = &proposal->algorithms[algorithm];
+	/* find end, and check for duplicates */
+	while ((*end) != NULL) {
+		/*
+		 * enckeylen=0 acts as a wildcard
+		 */
+		if (alg == (*end)->desc &&
+		    (alg->algo_type != IKE_ALG_ENCRYPT ||
+		     ((*end)->enckeylen == 0 ||
+		      enckeylen == (*end)->enckeylen))) {
+			parser->policy->warning("discarding duplicate algorithm '%s'",
+						alg->name);
+			return;
+		}
+		end = &(*end)->next;
+	}
+	struct algorithm new_algorithm = {
+		.desc = alg,
+		.enckeylen = enckeylen,
+	};
+	*end = clone_thing(new_algorithm, "alg");
+}
+
+void fmt_proposal(struct lswlog *log,
+		  const struct proposal *proposal)
+{
+ 	const char *ps = "";
+
+ 	const char *as = "";
+
+	as = ps;
+	FOR_EACH_ALGORITHM(proposal, encrypt, alg) {
+		const struct encrypt_desc *encrypt = encrypt_desc(alg->desc);
+		lswlogs(log, as); ps = "-"; as = "+";
+		lswlogs(log, encrypt->common.fqn);
+		if (alg->enckeylen != 0) {
+			lswlogf(log, "_%d", alg->enckeylen);
+		}
+	}
+
+	as = ps;
+	FOR_EACH_ALGORITHM(proposal, prf, alg) {
+		const struct prf_desc *prf = prf_desc(alg->desc);
+		lswlogs(log, as); ps = "-"; as = "+";
+		lswlogs(log, prf->common.fqn);
+	}
+
+	as = ps;
+	if ((proposal->algorithms[PROPOSAL_prf] == NULL &&
+	     proposal->algorithms[PROPOSAL_integ] != NULL) ||
+	    (IMPAIR(PROPOSAL_PARSER) &&
+	     proposal->algorithms[PROPOSAL_integ] != NULL)) {
+		FOR_EACH_ALGORITHM(proposal, integ, alg) {
+			const struct integ_desc *integ = integ_desc(alg->desc);
+			lswlogs(log, as); ps = "-"; as = "+";
+			lswlogs(log, integ->common.fqn);
+		}
+	}
+
+	as = ps;
+	FOR_EACH_ALGORITHM(proposal, dh, alg) {
+		const struct oakley_group_desc *dh = dh_desc(alg->desc);
+		lswlogs(log, as); ps = "-"; as = "+";
+		lswlogs(log, dh->common.fqn);
+	}
+}
+
+void fmt_proposals(struct lswlog *log, const struct proposals *proposals)
+{
+	const char *sep = "";
+	FOR_EACH_PROPOSAL(proposals, proposal) {
+		lswlogs(log, sep);
+		fmt_proposal(log, proposal);
+		sep = ", ";
+	}
+}
+
+/*
+ * When PFS=no ignore any DH algorithms, and when PFS=yes reject
+ * mixing implict and explicit DH.
+ */
+static bool proposals_pfs_vs_dh_check(struct proposal_parser *parser,
+				      struct proposals *proposals)
+{
+	/* scrape the proposals for dh algorithms */
+	const struct proposal *first_null = NULL;
+	const struct proposal *first_none = NULL;
+	const struct ike_alg *first_dh = NULL;
+	const struct ike_alg *second_dh = NULL;
+	FOR_EACH_PROPOSAL(proposals, proposal) {
+		if (proposal->algorithms[PROPOSAL_dh] == NULL) {
+			if (first_null == NULL) {
+				first_null = proposal;
+			}
+		} else if (proposal->algorithms[PROPOSAL_dh]->desc == &ike_alg_dh_none.common) {
+			if (first_none == NULL) {
+				first_none = proposal;
+			}
+		} else if (first_dh == NULL) {
+			first_dh = proposal->algorithms[PROPOSAL_dh]->desc;
+		} else if (second_dh == NULL &&
+			   first_dh != proposal->algorithms[PROPOSAL_dh]->desc) {
+			second_dh = proposal->algorithms[PROPOSAL_dh]->desc;
+		}
+	}
+
+	if (first_dh == NULL && first_none == NULL) {
+		/* no DH is always ok */
+		return true;
+	}
+
+	/*
+	 * Try to generate very specific errors first.  For instance,
+	 * given PFS=no esp=aes,aes;dh21, an error stating that dh21
+	 * is not valid because of PFS is more helpful than an error
+	 * saying that all or no proposals need PFS.
+	 */
+
+	/*
+	 * Since PFS=NO overrides any DH, don't silently ignore it.
+	 * Check this early so that a conflict with PFS=no code gets
+	 * reported before anything else.
+	 */
+	if (!parser->policy->pfs && (first_dh != NULL || first_none != NULL)) {
+		FOR_EACH_PROPOSAL(proposals, proposal) {
+			const struct ike_alg *dh = NULL;
+			if (proposal->algorithms[PROPOSAL_dh] != NULL) {
+				dh = proposal->algorithms[PROPOSAL_dh]->desc;
+			}
+			if (dh == &ike_alg_dh_none.common) {
+				parser->policy->warning("ignoring redundant %s DH algorithm NONE as PFS policy is disabled",
+							parser->protocol->name);
+			} else if (dh != NULL) {
+				parser->policy->warning("ignoring %s DH algorithm %s as PFS policy is disabled",
+							parser->protocol->name,
+							dh->fqn);
+			}
+			free_algorithms(proposal, PROPOSAL_dh);
+		}
+		return true;
+	}
+
+	/*
+	 * Since at least one proposal included DH, all proposals
+	 * should.  A proposal without DH is an error.
+	 *
+	 * (The converse, no proposals including DH was handled right
+	 * at the start).
+	 */
+	if (first_null != NULL) {
+		/* DH was specified */
+		proposal_error(parser, "either all or no %s proposals should specify DH",
+			       parser->protocol->name);
+		if (!impair_proposal_errors(parser)) {
+			return false;
+		}
+	}
+
+	switch (parser->policy->version) {
+
+	case IKEv1:
+		/*
+		 * IKEv1 only allows one DH algorithm.
+		 */
+		if (first_dh != NULL && second_dh != NULL) {
+			proposal_error(parser, "more than one IKEv1 %s DH algorithm (%s, %s) is not allowed in quick mode",
+				       parser->protocol->name,
+				       first_dh->fqn,
+				       second_dh->fqn);
+			if (!impair_proposal_errors(parser)) {
+				return false;
+			}
+		}
+		break;
+
+	case IKEv2:
+		/*
+		 * IKEv2, only implements one DH algorithm.
+		 */
+		if (first_dh != NULL && second_dh != NULL) {
+			proposal_error(parser, "more than one IKEv2 %s DH algorithm (%s, %s) requires unimplemented CHILD_SA INVALID_KE",
+				       parser->protocol->name,
+				       first_dh->fqn,
+				       second_dh->fqn);
+			if (!impair_proposal_errors(parser)) {
+				return false;
+			}
+		}
+		break;
+
+	default:
+		/* ignore */
+		break;
+	}
+
+	return true;
+}
+
+void proposal_error(struct proposal_parser *parser, const char *fmt, ...)
+{
+	va_list ap;
+	va_start(ap, fmt);
+	vsnprintf(parser->error, sizeof(parser->error), fmt, ap);
+	va_end(ap);
+}
+
+bool impair_proposal_errors(struct proposal_parser *parser)
+{
+	pexpect(parser->error[0] != '\0');
+	if (IMPAIR(PROPOSAL_PARSER)) {
+		libreswan_log("IMPAIR: ignoring proposal error: %s",
+			      parser->error);
+		parser->error[0] = '\0';
+		return true;
+	} else {
+		return false;
+	}
+}
+
+struct proposals *proposals_from_str(struct proposal_parser *parser,
+				     const char *str)
+{
+	struct proposals *proposals = alloc_thing(struct proposals, "proposals");
+	unsigned parser_version = parser->policy->parser_version;
+	if (parser_version == 0) {
+		parser_version = parser->policy->version;
+	}
+	bool ok;
+	switch (parser_version) {
+	case 2: ok = v2_proposals_parse_str(parser, proposals, shunk1(str)); break;
+	default: ok = v1_proposals_parse_str(parser, proposals, shunk1(str)); break;
+	}
+	if (!ok) {
+		proposals_delref(&proposals);
+		return NULL;
+	}
+	if (proposals->proposals == NULL) {
+		proposals_delref(&proposals);
+		return NULL;
+	}
+	if (parser->policy->check_pfs_vs_dh &&
+	    !proposals_pfs_vs_dh_check(parser, proposals)) {
+		pexpect(parser->error[0] != '\0');
+		proposals_delref(&proposals);
+		return NULL;
+	}
+	return proposals;
+}
diff -Naur libreswan-3.27-orig/lib/libswan/shunk.c libreswan-3.27/lib/libswan/shunk.c
--- libreswan-3.27-orig/lib/libswan/shunk.c	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/lib/libswan/shunk.c	2019-02-15 16:32:28.982835370 -0500
@@ -1,6 +1,6 @@
-/* string fragments, for libreswan
+/* Constant string (octet) fragments, for libreswan
  *
- * Copyright (C) 2018 Andrew Cagney
+ * Copyright (C) 2018-2019 Andrew Cagney
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -20,12 +20,17 @@
 
 #include "shunk.h"
 
-const shunk_t empty_shunk;
+/*
+ * Don't mistake a NULL_SHUNK for an empty shunk - just like for
+ * strings they are different.
+ */
+
+const shunk_t null_shunk = NULL_SHUNK;
 
 shunk_t shunk1(const char *ptr)
 {
 	if (ptr == NULL) {
-		return empty_shunk;
+		return null_shunk;
 	} else {
 		return shunk2(ptr, strlen(ptr));
 	}
@@ -34,28 +39,37 @@
 shunk_t shunk2(const char *ptr, int len)
 {
 	/*
-	 * Since a zero length string and a NULL string pointer are
-	 * considered to be different, don't convert the former into
-	 * an empty_chunk.
+	 * Since a zero length string is not the same as a NULL
+	 * string, don't try to be smart and convert the former into
+	 * the latter.
 	 */
 	return (shunk_t) { .ptr = ptr, .len = len, };
 }
 
-shunk_t shunk_strsep(shunk_t *shunk, const char *delim)
+shunk_t shunk_strsep(shunk_t *input, const char *delim)
 {
-	shunk_t token = shunk2(shunk->ptr, 0);
-	while (shunk->len > 0) {
-		if (strchr(delim, *shunk->ptr) != NULL) {
+	/*
+	 * If INPUT is NULL, the loop is skipped and NULL is
+	 * returned.
+	 */
+	shunk_t token = shunk2(input->ptr, 0);
+	while (input->len > 0) {
+		if (strchr(delim, *input->ptr) != NULL) {
 			/* discard delim */
-			shunk->ptr++;
-			shunk->len--;
+			input->ptr++;
+			input->len--;
 			return token;
 		}
 		/* advance, transfering the char */
 		token.len++;
-		shunk->ptr++;
-		shunk->len--;
+		input->ptr++;
+		input->len--;
 	}
+	/*
+	 * Flag this as the last token by setting INPUT to NULL; next
+	 * call will return the NULL shunk.
+	 */
+	*input = null_shunk;
 	return token;
 }
 
diff -Naur libreswan-3.27-orig/lib/libswan/v1_proposals.c libreswan-3.27/lib/libswan/v1_proposals.c
--- libreswan-3.27-orig/lib/libswan/v1_proposals.c	1969-12-31 19:00:00.000000000 -0500
+++ libreswan-3.27/lib/libswan/v1_proposals.c	2019-02-15 16:47:22.485144451 -0500
@@ -0,0 +1,578 @@
+/* V1 algorithm proposal parsing, for libreswan
+ *
+ * Copyright (C) 2012 Paul Wouters <paul@libreswan.org>
+ * Copyright (C) 2015-2019 Andrew Cagney
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <https://www.gnu.org/licenses/gpl2.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+
+#include "lswlog.h"
+#include "lswalloc.h"
+#include "constants.h"
+#include "proposals.h"
+#include "ike_alg.h"
+#include "ike_alg_integ.h"
+#include "ike_alg_dh.h"
+#include "alg_byname.h"
+
+/*
+ * Add the proposal defaults for the specific algorithm.
+ */
+
+typedef struct v1_proposal merge_alg_default_t(struct v1_proposal proposal,
+					       const struct ike_alg *default_alg);
+
+static struct v1_proposal merge_dh_default(struct v1_proposal proposal,
+					   const struct ike_alg *default_alg)
+{
+	proposal.dh = oakley_group_desc(default_alg);
+	return proposal;
+}
+
+static struct v1_proposal merge_encrypt_default(struct v1_proposal proposal,
+						const struct ike_alg *default_alg)
+{
+	proposal.encrypt = encrypt_desc(default_alg);
+	return proposal;
+}
+
+static struct v1_proposal merge_prf_default(struct v1_proposal proposal,
+					    const struct ike_alg *default_alg)
+{
+	proposal.prf = prf_desc(default_alg);
+	return proposal;
+}
+
+static struct v1_proposal merge_integ_default(struct v1_proposal proposal,
+					      const struct ike_alg *default_alg)
+{
+	proposal.integ = integ_desc(default_alg);
+	return proposal;
+}
+
+static bool add_proposal_defaults(struct proposal_parser *parser,
+				  const struct proposal_defaults *defaults,
+				  struct proposals *proposals,
+				  const struct v1_proposal *proposal);
+
+static bool add_alg_defaults(struct proposal_parser *parser,
+			     const struct proposal_defaults *defaults,
+			     struct proposals *proposals,
+			     const struct v1_proposal *proposal,
+			     const struct ike_alg_type *type,
+			     const struct ike_alg **default_algs,
+			     merge_alg_default_t *merge_alg_default)
+{
+	/*
+	 * Use VALID_ALG to add the valid algorithms into VALID_ALGS.
+	 */
+	for (const struct ike_alg **default_alg = default_algs;
+	     *default_alg; default_alg++) {
+		const struct ike_alg *alg = *default_alg;
+		if (!alg_byname_ok(parser, alg,
+				   shunk1(alg->name))) {
+			DBG(DBG_PROPOSAL_PARSER,
+			    DBG_log("skipping default %s",
+				    parser->error));
+			parser->error[0] = '\0';
+			continue;
+		}
+		/* add it */
+		DBG(DBG_PROPOSAL_PARSER,
+		    DBG_log("adding default %s %s",
+			    ike_alg_type_name(type),
+			    alg->name));
+		struct v1_proposal merged_proposal = merge_alg_default(*proposal,
+									 *default_alg);
+		if (!add_proposal_defaults(parser, defaults,
+					   proposals, &merged_proposal)) {
+			passert(parser->error[0] != '\0');
+			return false;
+		}
+	}
+	return true;
+}
+
+/*
+ * Validate the proposal and, suppressing duplicates, add it to the
+ * proposal list.
+ */
+
+static bool add_proposal(struct proposal_parser *parser,
+			 struct proposals *proposals,
+			 const struct v1_proposal *proposal)
+{
+	struct proposal *new = alloc_proposal(parser);
+	if (proposal->encrypt != NULL) {
+		append_algorithm(parser, new, PROPOSAL_encrypt,
+				 &proposal->encrypt->common,
+				 proposal->enckeylen);
+	}
+#define A(NAME)								\
+	if (proposal->NAME != NULL) {					\
+		append_algorithm(parser, new, PROPOSAL_##NAME,		\
+				 &proposal->NAME->common, 0);		\
+	}
+	A(prf);
+	A(integ);
+	A(dh);
+#undef A
+	/* back end? */
+	if (!proposal->protocol->proposal_ok(parser, new)) {
+		free_proposal(&new);
+		return false;
+	}
+	append_proposal(proposals, &new);
+	return true;
+}
+
+/*
+ * For all the algorithms, when an algorithm is missing (NULL), and
+ * there are defaults, add them.
+ */
+
+static bool add_proposal_defaults(struct proposal_parser *parser,
+				  const struct proposal_defaults *defaults,
+				  struct proposals *proposals,
+				  const struct v1_proposal *proposal)
+{
+	/*
+	 * Note that the order in which things are recursively added -
+	 * MODP, ENCR, PRF/HASH - affects test results.  It determines
+	 * things like the order of proposals.
+	 */
+	if (proposal->dh == NULL &&
+	    defaults != NULL && defaults->dh != NULL) {
+		return add_alg_defaults(parser, defaults,
+					proposals, proposal,
+					&ike_alg_dh, defaults->dh,
+					merge_dh_default);
+	} else if (proposal->encrypt == NULL &&
+		   defaults != NULL && defaults->encrypt != NULL) {
+		return add_alg_defaults(parser, defaults,
+					proposals, proposal,
+					&ike_alg_encrypt, defaults->encrypt,
+					merge_encrypt_default);
+	} else if (proposal->prf == NULL &&
+		   defaults != NULL && defaults->prf != NULL) {
+		return add_alg_defaults(parser, defaults,
+					proposals, proposal,
+					&ike_alg_prf, defaults->prf,
+					merge_prf_default);
+	} else if (proposal->integ == NULL &&
+		   proposal->encrypt != NULL &&
+		   encrypt_desc_is_aead(proposal->encrypt)) {
+		/*
+		 * Since AEAD, integrity is always 'none'.
+		 */
+		struct v1_proposal merged_proposal = *proposal;
+		merged_proposal.integ = &ike_alg_integ_none;
+		return add_proposal_defaults(parser, defaults,
+					     proposals, &merged_proposal);
+	} else if (proposal->integ == NULL &&
+		   defaults != NULL && defaults->integ != NULL) {
+		return add_alg_defaults(parser, defaults,
+					proposals, proposal,
+					&ike_alg_integ, defaults->integ,
+					merge_integ_default);
+	} else if (proposal->integ == NULL &&
+		   proposal->prf != NULL &&
+		   proposal->encrypt != NULL &&
+		   !encrypt_desc_is_aead(proposal->encrypt)) {
+		/*
+		 * Since non-AEAD, use an integrity algorithm that is
+		 * implemented using the PRF.
+		 */
+		struct v1_proposal merged_proposal = *proposal;
+		for (const struct integ_desc **algp = next_integ_desc(NULL);
+		     algp != NULL; algp = next_integ_desc(algp)) {
+			const struct integ_desc *alg = *algp;
+			if (alg->prf == proposal->prf) {
+				merged_proposal.integ = alg;
+				break;
+			}
+		}
+		if (merged_proposal.integ == NULL) {
+			proposal_error(parser, "%s integrity derived from PRF '%s' is not supported",
+				       proposal->protocol->name,
+				       proposal->prf->common.name);
+			return false;
+		}
+		return add_proposal_defaults(parser, defaults,
+					     proposals, &merged_proposal);
+	} else {
+		return add_proposal(parser, proposals, proposal);
+	}
+}
+
+static bool merge_default_proposals(struct proposal_parser *parser,
+				    struct proposals *proposals,
+				    const struct v1_proposal *proposal)
+{
+	/*
+	 * If there's a hint of IKEv1 being enabled then prefer its
+	 * larger set of defaults.
+	 *
+	 * This should increase the odds of both ends interoperating.
+	 *
+	 * For instance, the IKEv2 defaults were preferred and one end
+	 * has ikev2=never then, in aggressive mode, things don't
+	 * work.
+	 */
+	passert(parser->policy->version < elemsof(proposal->protocol->defaults));
+	const struct proposal_defaults *defaults =
+		proposal->protocol->defaults[parser->policy->version];
+	return add_proposal_defaults(parser, defaults,
+				     proposals, proposal);
+}
+
+static const struct ike_alg *lookup_byname(struct proposal_parser *parser,
+					   alg_byname_fn *alg_byname,
+					   shunk_t name,
+					   size_t key_bit_length,
+					   shunk_t print_name,
+					   const char *what)
+{
+	if (name.len > 0) {
+		if (alg_byname != NULL) {
+			const struct ike_alg *alg = alg_byname(parser, name, key_bit_length,
+							       print_name);
+			if (alg == NULL) {
+				DBG(DBG_PROPOSAL_PARSER,
+				    DBG_log("%s_byname('"PRI_SHUNK"') failed: %s",
+					    what, PRI_shunk(name),
+					    parser->error));
+				passert(parser->error[0] != '\0');
+				return NULL;
+			}
+			DBG(DBG_PROPOSAL_PARSER,
+			    DBG_log("%s_byname('"PRI_SHUNK"') returned '%s'",
+				    what, PRI_shunk(name), alg->name));
+			return alg;
+		} else {
+			DBG(DBG_PROPOSAL_PARSER,
+			    DBG_log("ignoring %s '"PRI_SHUNK"'",
+				    what, PRI_shunk(name)));
+			return NULL;
+		}
+	}
+	return NULL;
+}
+
+static int parse_eklen(struct proposal_parser *parser, shunk_t buf)
+{
+	/* convert -<eklen> if present */
+	char *end = NULL;
+	long eklen = strtol(buf.ptr, &end, 10);
+	if (buf.ptr + buf.len != end) {
+		proposal_error(parser, "encryption key length '"PRI_SHUNK"' contains a non-numeric character",
+			       PRI_shunk(buf));
+		return 0;
+	}
+	if (eklen >= INT_MAX) {
+		proposal_error(parser, "encryption key length '"PRI_SHUNK"' WAY too big",
+			       PRI_shunk(buf));
+		return 0;
+	}
+	if (eklen == 0) {
+		proposal_error(parser, "encryption key length is zero");
+		return 0;
+	}
+	return eklen;
+}
+
+/*
+ * Try to parse any of <ealg>-<ekeylen>, <ealg>_<ekeylen>,
+ * <ealg><ekeylen>, or <ealg>.  Strings like aes_gcm_16 and
+ * aes_gcm_16_256 end up in alg[0], while strings like aes_gcm_16-256
+ * end up in alg[0]-alg[1].
+ */
+
+struct token {
+	char sep;
+	shunk_t alg;
+};
+
+static bool parse_encrypt(struct proposal_parser *parser,
+			  struct token **tokens,
+			  struct v1_proposal *proposal)
+{
+	shunk_t ealg = (*tokens)[0].alg;
+	shunk_t eklen = (*tokens)[1].alg;
+	if (eklen.len > 0 && isdigit(eklen.ptr[0])) {
+		/* assume <ealg>-<eklen> */
+		int enckeylen = parse_eklen(parser, eklen);
+		if (enckeylen <= 0) {
+			passert(parser->error[0] != '\0');
+			return false;
+		}
+		/* print <alg>-<len> */
+		shunk_t print_name = shunk2(ealg.ptr, eklen.ptr + eklen.len - ealg.ptr);
+		proposal->enckeylen = enckeylen;
+		proposal->encrypt =
+			encrypt_desc(lookup_byname(parser,
+						   encrypt_alg_byname,
+						   ealg, proposal->enckeylen,
+						   print_name, "encryption"));
+		/* Was <ealg>-<eklen> rejected? */
+		if (parser->error[0] != '\0') {
+			return false;
+		}
+		*tokens += 2; /* consume both tokens */
+		return true;
+	}
+	/* try <ealg> */
+	shunk_t print_name = ealg;
+	proposal->encrypt =
+		encrypt_desc(lookup_byname(parser,
+					   encrypt_alg_byname,
+					   ealg, proposal->enckeylen,
+					   print_name, "encryption"));
+	if (parser->error[0] != '\0') {
+		/*
+		 * Could it be <ealg><eklen> or <ealg>_<eklen>?  Work
+		 * backwards skipping any digits.
+		 */
+		shunk_t end = shunk2(ealg.ptr + ealg.len, 0);
+		while (end.ptr > ealg.ptr && isdigit(end.ptr[-1])) {
+			end.ptr--;
+			end.len++;
+		}
+		if (end.len == 0) {
+			/*
+			 * no trailing <eklen> and <ealg> was rejected
+			 */
+			passert(parser->error[0] != '\0');
+			return false;
+		}
+		/* try to convert */
+		int enckeylen = parse_eklen(parser, end);
+		if (enckeylen <= 0) {
+			passert(parser->error[0] != '\0');
+			return false;
+		}
+		proposal->enckeylen = enckeylen;
+		/*
+		 * trim <eklen> from <ealg>; and then trim any
+		 * trailing '_'
+		 */
+		ealg.len = end.ptr - ealg.ptr;
+		if (end.ptr > ealg.ptr && end.ptr[-1] == '_') {
+			ealg.len -= 1;
+		}
+		/* try again */
+		parser->error[0] = '\0';
+		proposal->encrypt =
+			encrypt_desc(lookup_byname(parser,
+						   encrypt_alg_byname,
+						   ealg, proposal->enckeylen,
+						   print_name, "encryption"));
+		if (parser->error[0] != '\0') {
+			return false;
+		}
+	}
+	*tokens += 1; /* consume one token */
+	return true;
+}
+
+static bool parser_proposals_add(struct proposal_parser *parser,
+				 struct token *tokens, struct v1_proposal proposal,
+				 struct proposals *proposals)
+{
+	LSWDBGP(DBG_PROPOSAL_PARSER, buf) {
+		lswlogs(buf, "algs:");
+		for (struct token *token = tokens; token->alg.ptr != NULL; token++) {
+			lswlogf(buf, " algs[%tu] = '"PRI_SHUNK"'",
+				token - tokens, PRI_shunk(token->alg));
+		}
+	}
+
+	bool lookup_encrypt = parser->protocol->encrypt_alg_byname != NULL;
+	if (!lookup_encrypt && IMPAIR(PROPOSAL_PARSER)) {
+		/* Force lookup, will discard any error. */
+		lookup_encrypt = true;
+	}
+	if (lookup_encrypt && tokens->alg.ptr != NULL && tokens->sep != ';') {
+		if (!parse_encrypt(parser, &tokens, &proposal)) {
+			if (IMPAIR(PROPOSAL_PARSER)) {
+				/* ignore the lookup and stumble on */
+				parser->error[0] = '\0';
+			} else {
+				passert(parser->error[0] != '\0');
+				return false;
+			}
+		}
+	}
+
+	bool lookup_prf = parser->protocol->prf_alg_byname != NULL;
+	if (!lookup_prf && IMPAIR(PROPOSAL_PARSER)) {
+		/*
+		 * When impaired, only force PRF lookup when the the
+		 * token after this one is a valid INTEG algorithm.
+		 * Otherwise something like ah=sha1 gets parsed as
+		 * ah=[encr]-sha1-[integ]-[dh] instead of
+		 * ah=[encr]-[prf]-sha1-[dh].
+		 */
+		shunk_t prf = tokens[0].alg;
+		shunk_t integ = tokens[1].alg;
+		if (prf.ptr != NULL && integ.ptr != NULL) {
+			lookup_prf = (lookup_byname(parser, integ_alg_byname,
+						    integ, 0, integ, "integrity")
+				      != NULL);
+			parser->error[0] = '\0';
+		}
+	}
+	if (lookup_prf && tokens->alg.ptr != NULL && tokens->sep != ';') {
+		shunk_t prf = tokens[0].alg;
+		proposal.prf = prf_desc(lookup_byname(parser,
+						      prf_alg_byname,
+						      prf, 0, prf, "PRF"));
+		if (parser->error[0] != '\0') {
+			return false;
+		}
+		tokens += 1; /* consume one arg */
+	}
+
+	/*
+	 * By default, don't allow IKE's [...]-<prf>-<integ>-[....].
+	 * Instead fill in integrity using the above PRF.
+	 *
+	 * XXX: The parser and output isn't consistent in that for ESP
+	 * it parses <encry>-<integ> but for IKE it parses
+	 * <encr>-<prf>.  This seems to lead to confusion when
+	 * printing proposals - ike=aes_gcm-sha1 gets mis-read as as
+	 * using sha1 as integrity.  ike-aes_gcm-none-sha1 would
+	 * clarify this but that makes for a fun parse.
+	 */
+	bool lookup_integ = (parser->protocol->prf_alg_byname == NULL &&
+			     parser->protocol->integ_alg_byname != NULL);
+	if (!lookup_integ && IMPAIR(PROPOSAL_PARSER)) {
+		/* force things */
+		lookup_integ = true;
+	}
+	if (lookup_integ && tokens->alg.ptr != NULL && tokens->sep != ';') {
+		shunk_t integ = tokens[0].alg;
+		proposal.integ = integ_desc(lookup_byname(parser,
+							  integ_alg_byname,
+							  integ, 0, integ, "integrity"));
+		if (parser->error[0] != '\0') {
+			if (tokens[1].alg.ptr != NULL) {
+				/*
+				 * This alg should have been
+				 * integrity, since the next would be
+				 * DH; error applies.
+				 */
+				passert(parser->error[0] != '\0');
+				return false;
+			}
+			if (tokens[1].alg.ptr == NULL &&
+			    parser->protocol->prf_alg_byname == NULL) {
+				/*
+				 * Only one arg, integrity is prefered
+				 * to DH (and no PRF); error applies.
+				 */
+				passert(parser->error[0] != '\0');
+				return false;
+			}
+			/* let DH try */
+			parser->error[0] = '\0';
+		} else {
+			tokens += 1; /* consume one arg */
+		}
+	}
+
+	bool lookup_dh = parser->protocol->dh_alg_byname || IMPAIR(PROPOSAL_PARSER);
+	if (lookup_dh && tokens->alg.ptr != NULL) {
+		shunk_t dh = tokens[0].alg;
+		proposal.dh = oakley_group_desc(lookup_byname(parser,
+							      dh_alg_byname,
+							      dh, 0,
+							      dh, "DH"));
+		if (parser->error[0] != '\0') {
+			return false;
+		}
+		tokens += 1; /* consume one arg */
+	}
+
+	if (tokens->alg.ptr != NULL) {
+		proposal_error(parser, "'"PRI_SHUNK"' unexpected",
+			       PRI_shunk(tokens[0].alg));
+		return false;
+	}
+
+	if (IMPAIR(PROPOSAL_PARSER)) {
+		return add_proposal(parser, proposals, &proposal);
+	} else {
+		return merge_default_proposals(parser, proposals, &proposal);
+	}
+}
+
+bool v1_proposals_parse_str(struct proposal_parser *parser,
+			    struct proposals *proposals,
+			    shunk_t alg_str)
+{
+	DBG(DBG_PROPOSAL_PARSER,
+	    DBG_log("parsing '"PRI_SHUNK"' for %s",
+		    PRI_shunk(alg_str), parser->protocol->name));
+
+	/* use default if no string */
+	if (alg_str.ptr == NULL) {
+		const struct v1_proposal proposal = {
+			.protocol = parser->protocol,
+		};
+		return merge_default_proposals(parser, proposals, &proposal);
+	}
+
+	if (alg_str.len == 0) {
+		/* XXX: hack to keep testsuite happy */
+		proposal_error(parser, "String ended with invalid char, just after \"\"");
+		return false;
+	}
+
+	shunk_t prop_ptr = alg_str;
+	do {
+		/* find the next proposal */
+		shunk_t prop = shunk_strsep(&prop_ptr, ",");
+		/* parse it */
+		struct token tokens[8];
+		zero(&tokens);
+		struct token *token = tokens;
+		char last_sep = '\0';
+		shunk_t alg_ptr = prop;
+		do {
+			if (token + 1 >= tokens+elemsof(tokens)) {
+				/* space for NULL? */
+				proposal_error(parser, "proposal too long");
+				return false;
+			}
+			/* find the next alg */
+			shunk_t alg = shunk_strsep(&alg_ptr, "-;,");
+			*token++ = (struct token) {
+				.alg = alg,
+				.sep = last_sep,
+			};
+			last_sep = alg.ptr[alg.len]; /* save separator */
+		} while (alg_ptr.len > 0);
+		struct v1_proposal proposal = {
+			.protocol = parser->protocol,
+		};
+		if (!parser_proposals_add(parser, tokens, proposal,
+					  proposals)) {
+			passert(parser->error[0] != '\0');
+			return false;
+		}
+	} while (prop_ptr.len > 0);
+	return true;
+}
diff -Naur libreswan-3.27-orig/lib/libswan/v2_proposals.c libreswan-3.27/lib/libswan/v2_proposals.c
--- libreswan-3.27-orig/lib/libswan/v2_proposals.c	1969-12-31 19:00:00.000000000 -0500
+++ libreswan-3.27/lib/libswan/v2_proposals.c	2019-02-15 16:49:23.278267768 -0500
@@ -0,0 +1,398 @@
+/* V2 algorithm proposal parsing, for libreswan
+ *
+ * Copyright (C) 2019 Andrew Cagney
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <https://www.gnu.org/licenses/gpl2.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+
+#include "lswlog.h"
+#include "lswalloc.h"
+#include "constants.h"
+#include "proposals.h"
+#include "ike_alg.h"
+#include "ike_alg_integ.h"
+#include "ike_alg_dh.h"
+#include "alg_byname.h"
+
+/*
+ * For all the algorithms, when an algorithm is missing (NULL), and
+ * there are defaults, add them.
+ */
+
+static void merge_algorithms(struct proposal_parser *parser,
+			     struct proposal *proposal,
+			     enum proposal_algorithm algorithm,
+			     const struct ike_alg **defaults)
+{
+	if (defaults == NULL) {
+		return;
+	}
+	if (next_algorithm(proposal, algorithm, NULL) != NULL) {
+		return;
+	}
+	for (const struct ike_alg **alg = defaults; (*alg) != NULL; alg++) {
+		append_algorithm(parser, proposal, algorithm, *alg, 0);
+	}
+}
+
+static bool merge_defaults(struct proposal_parser *parser,
+			   struct proposal *proposal)
+{
+	pexpect(parser->policy->version < elemsof(parser->protocol->defaults));
+	const struct proposal_defaults *defaults =
+		parser->protocol->defaults[parser->policy->version];
+	merge_algorithms(parser, proposal, PROPOSAL_encrypt, defaults->encrypt);
+	merge_algorithms(parser, proposal, PROPOSAL_prf, defaults->prf);
+	if (next_algorithm(proposal, PROPOSAL_integ, NULL) == NULL) {
+		if (proposal_encrypt_aead(proposal)) {
+			/*
+			 * Since AEAD, integrity is always 'none'.
+			 */
+			append_algorithm(parser, proposal, PROPOSAL_integ,
+					 &ike_alg_integ_none.common, 0);
+		} else if (defaults->integ != NULL) {
+			/*
+			 * Merge in the defaults.
+			 */
+			merge_algorithms(parser, proposal, PROPOSAL_integ,
+					 defaults->integ);
+		} else if (next_algorithm(proposal, PROPOSAL_prf, NULL) != NULL &&
+			   proposal_encrypt_norm(proposal)) {
+			/*
+			 * Since non-AEAD, use integrity algorithms
+			 * that are implemented using the PRFs.
+			 */
+			FOR_EACH_ALGORITHM(proposal, prf, prf) {
+				const struct integ_desc *integ = NULL;
+				for (const struct integ_desc **integp = next_integ_desc(NULL);
+				     integp != NULL; integp = next_integ_desc(integp)) {
+					if ((*integp)->prf != NULL &&
+					    &(*integp)->prf->common == prf->desc) {
+						integ = *integp;
+						break;
+					}
+				}
+				if (integ == NULL) {
+					proposal_error(parser, "%s integrity derived from PRF '%s' is not supported",
+						       parser->protocol->name,
+						       prf->desc->name);
+					return false;
+				}
+				append_algorithm(parser, proposal, PROPOSAL_integ,
+						 &integ->common, 0);
+			}
+		}
+	}
+	merge_algorithms(parser, proposal, PROPOSAL_dh, defaults->dh);
+	return true;
+}
+
+static bool parse_alg(struct proposal_parser *parser,
+		      struct proposal *proposal,
+		      enum proposal_algorithm algorithm,
+		      alg_byname_fn *alg_byname,
+		      shunk_t token, int enckeylen, shunk_t print,
+		      const char *what)
+{
+	if (alg_byname == NULL) {
+		/* n/a */
+		return false;
+	}
+	if (token.len == 0) {
+		/* will error at end */
+		return false;
+	}
+	const struct ike_alg *alg = alg_byname(parser, token, enckeylen, print);
+	if (alg == NULL) {
+		if (DBGP(DBG_PROPOSAL_PARSER)) {
+			DBG_log("%s_byname('"PRI_SHUNK"') failed: %s",
+				what, PRI_shunk(token),
+				parser->error);
+		}
+		pexpect(parser->error[0] != '\0');
+		return false;
+	}
+	DBGF(DBG_PROPOSAL_PARSER, "adding %s algorithm %s[_%d]",
+	     what, alg->name, enckeylen);
+	append_algorithm(parser, proposal, algorithm, alg, enckeylen);
+	return true;
+}
+
+/*
+ * tokenize <input> into <delim><alg><input>
+ */
+
+struct token {
+	char delim;
+	shunk_t alg;
+	shunk_t input;
+};
+
+static void next(struct token *token)
+{
+	if (token->delim == '\0') {
+		/* first call, set delim to something bogus */
+		token->delim = ' ';
+	} else {
+		token->delim = token->input.ptr != NULL ? token->input.ptr[-1] : ' ';
+	}
+	token->alg = shunk_strsep(&token->input, "-;+");
+	if (DBGP(DBG_PROPOSAL_PARSER)) {
+		if (token->alg.ptr == NULL) {
+			DBG_log("delim: n/a  alg: end-of-input");
+		} else {
+			DBG_log("delim: '%c' alg: '"PRI_SHUNK"'",
+				token->delim, PRI_shunk(token->alg));
+		}
+	}
+}
+
+/*
+ * Try to parse any of <ealg>-<ekeylen>, <ealg>_<ekeylen>,
+ * <ealg><ekeylen>, or <ealg> using some look-ahead.
+ */
+
+static int parse_eklen(struct proposal_parser *parser, shunk_t buf)
+{
+	/* convert -<eklen> if present */
+	char *end = NULL;
+	long eklen = strtol(buf.ptr, &end, 10);
+	if (buf.ptr + buf.len != end) {
+		proposal_error(parser, "encryption key length '"PRI_SHUNK"' contains a non-numeric character",
+			       PRI_shunk(buf));
+		return 0;
+	}
+	if (eklen >= INT_MAX) {
+		proposal_error(parser, "encryption key length '"PRI_SHUNK"' WAY too big",
+			       PRI_shunk(buf));
+		return 0;
+	}
+	if (eklen == 0) {
+		proposal_error(parser, "encryption key length is zero");
+		return 0;
+	}
+	return eklen;
+}
+
+static bool parse_encrypt(struct proposal_parser *parser,
+			  struct proposal *proposal, struct token *token)
+{
+	alg_byname_fn *alg_byname = parser->protocol->encrypt_alg_byname;
+	if (alg_byname == NULL) {
+		return false;
+	}
+	if (token->alg.len == 0) {
+		return false;
+	}
+	shunk_t ealg = token->alg;
+	/* try <ealg=token>-<eklen=lookahead> using look-ahead? */
+	struct token lookahead = *token;
+	next(&lookahead);
+	if (lookahead.delim == '-' &&
+	    lookahead.alg.len > 0 &&
+	    isdigit(lookahead.alg.ptr[0])) {
+		shunk_t eklen = lookahead.alg;
+		/* assume <ealg>-<eklen> */
+		int enckeylen = parse_eklen(parser, eklen);
+		if (enckeylen <= 0) {
+			pexpect(parser->error[0] != '\0');
+			return false;
+		}
+		/* print "<ealg>-<eklen>" in errors */
+		shunk_t print_name = shunk2(ealg.ptr, eklen.ptr + eklen.len - ealg.ptr);
+		if (!parse_alg(parser, proposal, PROPOSAL_encrypt, alg_byname,
+			       ealg, enckeylen, print_name, "encrypt")) {
+			return false;
+		}
+		*token = lookahead;
+		return true;
+	}
+	/* try <ealg> (no key len) */
+	shunk_t print_name = token->alg;
+	if (!parse_alg(parser, proposal, PROPOSAL_encrypt, alg_byname,
+		       ealg, 0, print_name, "encrypt")) {
+		/*
+		 * Could it be <ealg><eklen> or <ealg>_<eklen>?  Work
+		 * backwards skipping any digits.
+		 */
+		shunk_t end = shunk2(ealg.ptr + ealg.len, 0);
+		while (end.ptr > ealg.ptr && isdigit(end.ptr[-1])) {
+			end.ptr--;
+			end.len++;
+		}
+		if (end.len == 0) {
+			/*
+			 * no trailing <eklen> and <ealg> was rejected
+			 */
+			pexpect(parser->error[0] != '\0');
+			return false;
+		}
+		/* try to convert */
+		int enckeylen = parse_eklen(parser, end);
+		if (enckeylen <= 0) {
+			pexpect(parser->error[0] != '\0');
+			return false;
+		}
+		/*
+		 * trim <eklen> from <ealg>; and then trim any
+		 * trailing '_'
+		 */
+		ealg.len = end.ptr - ealg.ptr;
+		if (end.ptr > ealg.ptr && end.ptr[-1] == '_') {
+			ealg.len -= 1;
+		}
+		/* try again */
+		if (!parse_alg(parser, proposal, PROPOSAL_encrypt, alg_byname,
+			       ealg, enckeylen, print_name, "encrypt")) {
+			return false;
+		}
+	}
+	return true;
+}
+
+static bool parse_proposal(struct proposal_parser *parser,
+			   struct proposals *proposals UNUSED, shunk_t input)
+{
+	if (DBGP(DBG_PROPOSAL_PARSER)) {
+		DBG_log("proposal: '"PRI_SHUNK"'", PRI_shunk(input));
+	}
+
+	char error[sizeof(parser->error)] = "";
+	struct proposal *proposal = alloc_proposal(parser);
+
+	struct token token = {
+		.input = input,
+	};
+	next(&token);
+	/*
+	 * Encryption is not optional.
+	 */
+	bool lookup_encrypt = parser->protocol->encrypt_alg_byname != NULL;
+	if (lookup_encrypt) {
+		if (!parse_encrypt(parser, proposal, &token)) {
+			free_proposal(&proposal);
+			return false;
+		}
+		error[0] = parser->error[0] = '\0';
+		next(&token);
+		while (token.delim == '+' &&
+		       parse_encrypt(parser, proposal, &token)) {
+			error[0] = parser->error[0] = '\0';
+			next(&token);
+		}
+	}
+#define PARSE_ALG(STOP, ALG)						\
+	if (error[0] == '\0' && parser->error[0] != '\0') {		\
+		strcpy(error, parser->error);				\
+		DBGF(DBG_PROPOSAL_PARSER, "saved first error: %s", error); \
+	}								\
+	if (token.delim != STOP &&					\
+	    parse_alg(parser, proposal, PROPOSAL_##ALG,			\
+		      parser->protocol->ALG##_alg_byname,		\
+		      token.alg, 0, token.alg, #ALG)) {			\
+		error[0] = parser->error[0] = '\0';			\
+		next(&token);						\
+		while (token.delim == '+' &&				\
+		       parse_alg(parser, proposal, PROPOSAL_##ALG,	\
+				 parser->protocol->ALG##_alg_byname,	\
+				 token.alg, 0, token.alg, #ALG)) {	\
+			error[0] = parser->error[0] = '\0';		\
+			next(&token);					\
+		}							\
+	}
+	PARSE_ALG(';', prf);
+	/*
+	 * By default, don't allow ike=...-<prf>-<integ>-... but do
+	 * allow esp=...-<integ>.  In the case of IKE, when integrity
+	 * is required, it is filled in using the PRF.
+	 *
+	 * XXX: The parser and output isn't consistent in that for ESP
+	 * it parses <encry>-<integ> but for IKE it parses
+	 * <encr>-<prf>.  This seems to lead to confusion when
+	 * printing proposals - ike=aes_gcm-sha1 gets mis-read as as
+	 * using sha1 as integrity.  ike-aes_gcm-none-sha1 would
+	 * clarify this but that makes for a fun parse.
+	 */
+	if (parser->protocol->prf_alg_byname == NULL ||
+	    IMPAIR(PROPOSAL_PARSER)) {
+		PARSE_ALG(';', integ);
+	}
+	PARSE_ALG('\0', dh);
+	if (error[0] != '\0') {
+		DBGF(DBG_PROPOSAL_PARSER, "return first error: %s", error);
+		free_proposal(&proposal);
+		strcpy(parser->error, error);
+		return false;
+	}
+	if (parser->error[0] != '\0') {
+		DBGF(DBG_PROPOSAL_PARSER, "return last error: %s", parser->error);
+		free_proposal(&proposal);
+		return false;
+	}
+	if (token.alg.ptr != NULL) {
+		proposal_error(parser, "'"PRI_SHUNK"' unexpected",
+			 PRI_shunk(token.alg));
+		free_proposal(&proposal);
+		return false;
+	}
+	if (!IMPAIR(PROPOSAL_PARSER) &&
+	    !merge_defaults(parser, proposal)) {
+		free_proposal(&proposal);
+		return false;
+	}
+	/* back end? */
+	if (!parser->protocol->proposal_ok(parser, proposal)) {
+		free_proposal(&proposal);
+		return false;
+	}
+	append_proposal(proposals, &proposal);
+	return true;
+}
+
+bool v2_proposals_parse_str(struct proposal_parser *parser,
+			    struct proposals *proposals,
+			    shunk_t input)
+{
+	DBG(DBG_PROPOSAL_PARSER,
+	    DBG_log("parsing '"PRI_SHUNK"' for %s",
+		    PRI_shunk(input), parser->protocol->name));
+
+	/* use default if no string */
+	if (input.ptr == NULL) {
+		struct proposal *proposal = alloc_proposal(parser);
+		if (!merge_defaults(parser, proposal)) {
+			free_proposal(&proposal);
+			return false;
+		}
+		append_proposal(proposals, &proposal);
+		return true;
+	}
+
+	if (input.len == 0) {
+		/* XXX: hack to keep testsuite happy */
+		proposal_error(parser, "String ended with invalid char, just after \"\"");
+		return false;
+	}
+
+	do {
+		/* find the next proposal */
+		shunk_t proposal = shunk_strsep(&input, ",");
+		if (!parse_proposal(parser, proposals, proposal)) {
+			pexpect(parser->error[0] != '\0');
+			return false;
+		}
+	} while (input.len > 0);
+	return true;
+}
diff -Naur libreswan-3.27-orig/programs/algparse/algparse.c libreswan-3.27/programs/algparse/algparse.c
--- libreswan-3.27-orig/programs/algparse/algparse.c	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/programs/algparse/algparse.c	2019-02-15 16:49:23.278267768 -0500
@@ -9,15 +9,15 @@
 #include "lswconf.h"
 
 #include "ike_alg.h"
-#include "alg_info.h"
+#include "proposals.h"
 
 static bool test_proposals = false;
 static bool test_algs = false;
 static bool verbose = false;
 static bool debug = false;
 static bool impair = false;
-static bool ikev1 = false;
-static bool ikev2 = false;
+static enum ike_version ike_version = IKEv2;
+static unsigned parser_version = 0;
 static bool fips = false;
 static bool pfs = false;
 static int failures = 0;
@@ -25,23 +25,23 @@
 enum status { PASSED = 0, FAILED = 1, ERROR = 126, };
 enum expect { FAIL = false, PASS = true, COUNT, };
 
-#define CHECK(TYPE,PARSE,OK) {						\
+#define CHECK(CHECK,PARSE,OK) {						\
 		struct proposal_policy policy = {			\
-			.ikev1 = ikev1,					\
-			.ikev2 = ikev2,					\
+			.version = ike_version,				\
+			.parser_version = parser_version,		\
 			.alg_is_ok = OK,				\
 			.pfs = pfs,					\
 			.warning = warning,				\
+			.check_pfs_vs_dh = CHECK,			\
 		};							\
 		printf("algparse ");					\
 		if (fips) {						\
 			printf("-fips ");				\
 		}							\
-		if (ikev1) {						\
-			printf("-v1 ");					\
-		}							\
-		if (ikev2) {						\
-			printf("-v2 ");					\
+		switch (ike_version) {					\
+		case IKEv1: printf("-v1 "); break;			\
+		case IKEv2: printf("-v2 "); break;			\
+		default: break;						\
 		}							\
 		if (pfs) {						\
 			printf("-pfs ");				\
@@ -52,21 +52,19 @@
 			printf("'%s=%s'\n", #PARSE, algstr);		\
 		}							\
 		fflush(NULL);						\
-		char err_buf[512] = "";	/* ??? big enough? */		\
-		struct alg_info_##TYPE *e =				\
-			alg_info_##PARSE##_create_from_str(&policy,	\
-							   algstr,	\
-							   err_buf,	\
-							   sizeof(err_buf)); \
-		if (e != NULL) {					\
-			passert(err_buf[0] == '\0');			\
-			FOR_EACH_PROPOSAL_INFO(&e->ai, proposal) {	\
+		struct proposal_parser *parser =			\
+			PARSE##_proposal_parser(&policy);		\
+		struct proposals *proposals =				\
+			proposals_from_str(parser, algstr);		\
+		if (proposals != NULL) {				\
+			pexpect(parser->error[0] == '\0');		\
+			FOR_EACH_PROPOSAL(proposals, proposal) {	\
 				LSWLOG_FILE(stdout, log) {		\
 					lswlogf(log, "\t");		\
-					lswlog_proposal_info(log, proposal); \
+					fmt_proposal(log, proposal);	\
 				}					\
 			}						\
-			alg_info_free(&e->ai);				\
+			proposals_delref(&proposals);			\
 			if (expected == FAIL) {				\
 				failures++;				\
 				fprintf(stderr,				\
@@ -76,8 +74,8 @@
 					algstr == NULL ? "" : algstr);	\
 			}						\
 		} else {						\
-			passert(err_buf[0]);				\
-			printf("\tERROR: %s\n", err_buf);		\
+			pexpect(parser->error[0]);			\
+			printf("\tERROR: %s\n", parser->error);		\
 			if (expected == PASS) {				\
 				failures++;				\
 				fprintf(stderr,				\
@@ -89,6 +87,7 @@
 				failures++;				\
 			}						\
 		}							\
+		free_proposal_parser(&parser);				\
 		fflush(NULL);						\
 	}
 
@@ -122,17 +121,17 @@
 
 static void esp(enum expect expected, const char *algstr)
 {
-	CHECK(esp, esp, kernel_alg_is_ok);
+	CHECK(true, esp, kernel_alg_is_ok);
 }
 
 static void ah(enum expect expected, const char *algstr)
 {
-	CHECK(esp, ah, kernel_alg_is_ok);
+	CHECK(true, ah, kernel_alg_is_ok);
 }
 
 static void ike(enum expect expected, const char *algstr)
 {
-	CHECK(ike, ike, ike_alg_is_ike);
+	CHECK(false, ike, ike_alg_is_ike);
 }
 
 typedef void (protocol_t)(enum expect expected, const char *);
@@ -203,8 +202,8 @@
 	esp(!fips, "3des-sha1;modp1024");
 	esp(!fips, "3des-sha1;modp1536");
 	esp(true, "3des-sha1;modp2048");
-	esp(!ikev1, "3des-sha1;dh21");
-	esp(!ikev1, "3des-sha1;ecp_521");
+	esp(ike_version == IKEv2, "3des-sha1;dh21");
+	esp(ike_version == IKEv2, "3des-sha1;ecp_521");
 	esp(false, "3des-sha1;dh23");
 	esp(false, "3des-sha1;dh24");
 	esp(true, "3des-sha1");
@@ -218,7 +217,7 @@
 	esp(true, "aes-sha384");
 	esp(true, "aes-sha512");
 	esp(true, "aes128-sha1");
-	esp(true, "aes128-aes_xcbc");
+	esp(!fips, "aes128-aes_xcbc");
 	esp(true, "aes192-sha1");
 	esp(true, "aes256-sha1");
 	esp(true, "aes256-sha");
@@ -300,18 +299,18 @@
 	esp(!fips, "twofish");
 
 	esp(!fips, "camellia_cbc_256-hmac_sha2_512_256;modp8192"); /* long */
-	esp(!fips, "null_auth_aes_gmac_256-null;modp8192"); /* long */
+	esp(true, "null_auth_aes_gmac_256-null;modp8192"); /* long */
 	esp(true, "3des-sha1;modp8192"); /* allow ';' when unambigious */
 	esp(true, "3des-sha1-modp8192"); /* allow '-' when unambigious */
 	esp(!pfs, "aes-sha1,3des-sha1;modp8192");
 	esp(true, "aes-sha1-modp8192,3des-sha1-modp8192"); /* silly */
 	esp(true, "aes-sha1-modp8192,aes-sha1-modp8192,aes-sha1-modp8192"); /* suppress duplicates */
 
-	esp(!ikev1, "aes;none");
-	esp(!ikev1 && !pfs, "aes;none,aes");
-	esp(!ikev1, "aes;none,aes;modp2048");
-	esp(!ikev1, "aes-sha1-none");
-	esp(!ikev1, "aes-sha1;none");
+	esp(ike_version == IKEv2, "aes;none");
+	esp(ike_version == IKEv2 && !pfs, "aes;none,aes");
+	esp(ike_version == IKEv2, "aes;none,aes;modp2048");
+	esp(ike_version == IKEv2, "aes-sha1-none");
+	esp(ike_version == IKEv2, "aes-sha1;none");
 
 	/*
 	 * should this be supported - for now man page says not
@@ -374,9 +373,9 @@
 	ah(true, "sha2_256");
 	ah(true, "sha2_384");
 	ah(true, "sha2_512");
-	ah(true, "aes_xcbc");
-	ah(!ikev1, "sha2-none");
-	ah(!ikev1, "sha2;none");
+	ah(!fips, "aes_xcbc");
+	ah(ike_version == IKEv2, "sha2-none");
+	ah(ike_version == IKEv2, "sha2;none");
 	ah(true, "sha1-modp8192,sha1-modp8192,sha1-modp8192"); /* suppress duplicates */
 	ah(impair, "aes-sha1");
 	ah(false, "vanityhash1");
@@ -400,12 +399,15 @@
 	ike(true, "3des;dh21");
 	ike(true, "3des-sha1;dh21");
 	ike(true, "3des-sha1-ecp_521");
-	ike(!ikev1, "aes_gcm");
+	ike(ike_version == IKEv2, "aes_gcm");
 	ike(true, "aes-sha1-modp8192,aes-sha1-modp8192,aes-sha1-modp8192"); /* suppress duplicates */
 	ike(false, "aes;none");
 	ike(false, "id2"); /* should be rejected; idXXX removed */
 	ike(false, "3des-id2"); /* should be rejected; idXXX removed */
 	ike(false, "aes_ccm"); /* ESP/AH only */
+	ike(impair, "aes_gcm-sha1-none-modp2048");
+	ike(impair, "aes_gcm+aes_gcm-sha1-none-modp2048");
+	ike(false, "aes+aes_gcm"); /* mixing AEAD and NORM encryption */
 }
 
 static void usage(void)
@@ -432,9 +434,8 @@
 		"\n"
 		"Additional options:\n"
 		"\n"
-		"    -v1 | -ikev1: require IKEv1 support\n"
-		"    -v2 | -ikev2: require IKEv2 support\n"
-		"         default: require either IKEv1 or IKEv2 support\n"
+		"    -v2 | -ikev2: configure for IKEv2 (default)\n"
+		"    -v1 | -ikev1: configure for IKEv1\n"
 		"    -pfs | -pfs=yes | -pfs=no: specify PFS (perfect forward privicy)\n"
 		"         default: no\n"
 		"    -fips | -fips=yes | -fips=no: force NSS's FIPS mode\n"
@@ -446,6 +447,8 @@
 		"    -v --verbose: be more verbose\n"
 		"    -d --debug: enable debug logging\n"
 		"    -i --impair: disable all algorithm parser checks\n"
+		"    -p1: simple parser\n"
+		"    -p2: complex parser\n"
 		"\n"
 		"Examples:\n"
 		"\n"
@@ -483,10 +486,14 @@
 			test_proposals = true;
 		} else if (streq(arg, "ta")) {
 			test_algs = true;
+		} else if (streq(arg, "p1")) {
+			parser_version = 1;
+		} else if (streq(arg, "p2")) {
+			parser_version = 2;
 		} else if (streq(arg, "v1") || streq(arg, "ikev1")) {
-			ikev1 = true;
+			ike_version = IKEv1;
 		} else if (streq(arg, "v2") || streq(arg, "ikev2")) {
-			ikev2 = true;
+			ike_version = IKEv2;
 		} else if (streq(arg, "pfs") || streq(arg, "pfs=yes") || streq(arg, "pfs=on")) {
 			pfs = true;
 		} else if (streq(arg, "pfs=no") || streq(arg, "pfs=off")) {
diff -Naur libreswan-3.27-orig/programs/pluto/connections.c libreswan-3.27/programs/pluto/connections.c
--- libreswan-3.27-orig/programs/pluto/connections.c	2019-02-15 16:31:43.031408048 -0500
+++ libreswan-3.27/programs/pluto/connections.c	2019-02-15 16:32:28.986835407 -0500
@@ -69,7 +69,7 @@
 #include "peerlog.h"
 #include "keys.h"
 #include "whack.h"
-#include "alg_info.h"
+#include "proposals.h"
 #include "spdb.h"
 #include "ike_alg.h"
 #include "kernel_alg.h"
@@ -352,17 +352,13 @@
 		sr = next_sr;
 	}
 
-	if (c->alg_info_ike != NULL) {
-		alg_info_delref(&c->alg_info_ike->ai);
-		c->alg_info_ike = NULL;
-	}
-	free_ikev2_proposals(&c->ike_proposals);
+	proposals_delref(&c->ike_proposals.p);
+	proposals_delref(&c->child_proposals.p);
 
-	if (c->alg_info_esp != NULL) {
-		alg_info_delref(&c->alg_info_esp->ai);
-		c->alg_info_esp = NULL;
-	}
-	free_ikev2_proposals(&c->esp_or_ah_proposals);
+	free_ikev2_proposals(&c->v2_ike_proposals);
+	free_ikev2_proposals(&c->v2_ike_auth_child_proposals);
+	free_ikev2_proposals(&c->v2_create_child_proposals);
+	c->v2_create_child_proposals_default_dh = NULL; /* static pointer */
 
 	pfree(c);
 }
@@ -807,11 +803,8 @@
 	}
 
 	/* increment references to algo's, if any */
-	if (c->alg_info_ike != NULL)
-		alg_info_addref(&c->alg_info_ike->ai);
-
-	if (c->alg_info_esp != NULL)
-		alg_info_addref(&c->alg_info_esp->ai);
+	proposals_addref(&c->ike_proposals.p);
+	proposals_addref(&c->child_proposals.p);
 
 	if (c->pool !=  NULL)
 		reference_addresspool(c);
@@ -1496,8 +1489,6 @@
 	c->connalias = wm->connalias;
 	c->dnshostname = wm->dnshostname;
 	c->policy = wm->policy;
-	c->alg_info_ike = NULL;
-	c->alg_info_esp = NULL;
 	c->sighash_policy = wm->sighash_policy;
 
 	if (NEVER_NEGOTIATE(c->policy)) {
@@ -1556,49 +1547,35 @@
 		/* IKE cipher suites */
 
 		if (!LIN(POLICY_AUTH_NEVER, wm->policy) && wm->ike != NULL) {
-			char err_buf[256] = "";	/* ??? big enough? */
 
 			const struct proposal_policy proposal_policy = {
-				.ikev1 = LIN(POLICY_IKEV1_ALLOW, wm->policy),
-				/*
-				 * logic needs to match pick_initiator()
-				 *
-				 * XXX: Once pluto is changed to IKEv1 XOR
-				 * IKEv2 it should be possible to move this
-				 * magic into pluto proper and instead pass a
-				 * simple boolean.
-				 */
-				.ikev2 = LIN(POLICY_IKEV2_PROPOSE | POLICY_IKEV2_ALLOW, wm->policy),
+				/* * logic needs to match pick_initiator() */
+				.version = LIN(POLICY_IKEV2_ALLOW, wm->policy) ? IKEv2 : IKEv1,
 				.alg_is_ok = ike_alg_is_ike,
 				.pfs = LIN(POLICY_PFS, wm->policy),
+				.check_pfs_vs_dh = false,
 				.warning = libreswan_log,
 			};
 
-			c->alg_info_ike = alg_info_ike_create_from_str(&proposal_policy, wm->ike,
-								    err_buf, sizeof(err_buf));
+			struct proposal_parser *parser = ike_proposal_parser(&proposal_policy);
+			c->ike_proposals.p = proposals_from_str(parser, wm->ike);
 
-			if (c->alg_info_ike == NULL) {
-				pexpect(err_buf[0]); /* something */
+			if (c->ike_proposals.p == NULL) {
+				pexpect(parser->error[0]); /* something */
 				loglog(RC_FATAL, "Failed to add connection \"%s\": ike string error: %s",
-					wm->name, err_buf);
+					wm->name, parser->error);
+				free_proposal_parser(&parser);
 				pfree(c);
 				return;
 			}
+			free_proposal_parser(&parser);
 
-			/* from here on, error returns should alg_info_free(&c->alg_info_ike->ai); */
+			/* from here on, error returns should alg_info_free(&c->ike_proposals->ai); */
 
 			LSWDBGP(DBG_CRYPT | DBG_CONTROL, buf) {
 				lswlogs(buf, "ike (phase1) algorithm values: ");
-				lswlog_alg_info(buf, &c->alg_info_ike->ai);
+				fmt_proposals(buf, c->ike_proposals.p);
 			};
-			if (c->alg_info_ike->ai.alg_info_cnt == 0) {
-				loglog(RC_FATAL,
-					"Failed to add connection \"%s\": got 0 transforms for ike=\"%s\"",
-					wm->name, wm->ike);
-				alg_info_free(&c->alg_info_ike->ai);
-				pfree(c);
-				return;
-			}
 		}
 
 		/* ESP or AH cipher suites (but not both) */
@@ -1607,10 +1584,7 @@
 			DBG(DBG_CONTROL,
 			    DBG_log("from whack: got --esp=%s", wm->esp));
 
-			char err_buf[256] = "";	/* ??? big enough? */
-
 			const struct proposal_policy proposal_policy = {
-				.ikev1 = LIN(POLICY_IKEV1_ALLOW, wm->policy),
 				/*
 				 * logic needs to match pick_initiator()
 				 *
@@ -1619,11 +1593,16 @@
 				 * magic into pluto proper and instead pass a
 				 * simple boolean.
 				 */
-				.ikev2 = LIN(POLICY_IKEV2_PROPOSE | POLICY_IKEV2_ALLOW, wm->policy),
+				.version = LIN(POLICY_IKEV2_ALLOW, wm->policy) ? IKEv2 : IKEv1,
 				.alg_is_ok = kernel_alg_is_ok,
 				.pfs = LIN(POLICY_PFS, wm->policy),
+				.check_pfs_vs_dh = true,
 				.warning = libreswan_log,
 			};
+			struct proposal_parser *(*fn)(const struct proposal_policy *policy) =
+				(c->policy & POLICY_ENCRYPT ? esp_proposal_parser :
+				ah_proposal_parser);
+			struct proposal_parser *parser = fn(&proposal_policy);
 
 			/*
 			 * We checked above that exactly one of
@@ -1632,42 +1611,22 @@
 			 * function is called (and those functions are
 			 * almost identical).
 			 */
-			c->alg_info_esp =
-				/* function: */
-					(c->policy & POLICY_ENCRYPT ?
-					 alg_info_esp_create_from_str :
-					 alg_info_ah_create_from_str)
-				/* arguments: */
-					(&proposal_policy,
-					 wm->esp, err_buf, sizeof(err_buf));
-
-			if (c->alg_info_esp == NULL) {
+			c->child_proposals.p = proposals_from_str(parser, wm->esp);
+			if (c->child_proposals.p == NULL) {
 				loglog(RC_FATAL,
 				       "Failed to add connection \"%s\", esp=\"%s\" is invalid: %s",
-				       wm->name, wm->esp, err_buf);
-				if (c->alg_info_ike != NULL)
-					alg_info_free(&c->alg_info_ike->ai);
+					wm->name, wm->esp, parser->error);
+				free_proposal_parser(&parser);
 				pfree(c);
 				return;
 			}
+			free_proposal_parser(&parser);
 
-			/* from here on, error returns should alg_info_free(&c->alg_info_esp->ai); */
-
-			if (c->alg_info_esp->ai.alg_info_cnt == 0) {
-				loglog(RC_FATAL,
-				       "Failed to add connection \"%s\", esp=\"%s\" contained 0 valid transforms",
-				       wm->name, wm->esp);
-				if (c->alg_info_ike != NULL)
-					alg_info_free(&c->alg_info_ike->ai);
-				if (c->alg_info_esp != NULL) \
-					alg_info_free(&c->alg_info_esp->ai);
-				pfree(c);
-				return;
-			}
+			/* from here on, error returns should alg_info_free(&c->child_proposals->ai); */
 
 			LSWDBGP(DBG_CONTROL, buf) {
 				lswlogs(buf, "ESP/AH string values: ");
-				lswlog_alg_info(buf, &c->alg_info_esp->ai);
+				fmt_proposals(buf, c->child_proposals.p);
 			};
 		}
 
diff -Naur libreswan-3.27-orig/programs/pluto/connections.h libreswan-3.27/programs/pluto/connections.h
--- libreswan-3.27-orig/programs/pluto/connections.h	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/programs/pluto/connections.h	2019-02-15 16:32:28.986835407 -0500
@@ -29,6 +29,7 @@
 #define CONNECTIONS_H
 
 #include "fd.h"
+#include "proposals.h"
 
 /* There are two kinds of connections:
  * - ISAKMP connections, between hosts (for IKE communication)
@@ -318,8 +319,8 @@
 	/* if multiple policies, next one to apply */
 	struct connection *policy_next;
 
-	struct alg_info_esp *alg_info_esp;	/* ??? OK for AH too? */
-	struct alg_info_ike *alg_info_ike;
+	struct ike_proposals ike_proposals;
+	struct child_proposals child_proposals;
 
 	/*
 	 * The ALG_INFO converted to IKEv2 format.
@@ -327,9 +328,23 @@
 	 * Since they are allocated on-demand so there's no need to
 	 * worry about copying them when a connection object gets
 	 * cloned.
+	 *
+	 * For a child SA, two different proposals are used:
+	 *
+	 * - during the IKE_AUTH exchange a proposal stripped of any
+	 *   DH (it uses keying material from the IKE SA's SKSEED).
+	 *
+	 * - during a CREATE_CHILD_SA exchange, a mash up of the
+	 *   proposal and the IKE SA's DH algorithm.  Since the IKE
+	 *   SA's DH can change, it too is saved so a rebuild can be
+	 *   triggered.
+	 *
+	 * XXX: has to be a better way?
 	 */
-	struct ikev2_proposals *ike_proposals;
-	struct ikev2_proposals *esp_or_ah_proposals;
+	struct ikev2_proposals *v2_ike_proposals;
+	struct ikev2_proposals *v2_ike_auth_child_proposals;
+	struct ikev2_proposals *v2_create_child_proposals;
+	const struct oakley_group_desc *v2_create_child_proposals_default_dh;
 
 	/* host_pair linkage */
 	struct host_pair *host_pair;
diff -Naur libreswan-3.27-orig/programs/pluto/crypto.c libreswan-3.27/programs/pluto/crypto.c
--- libreswan-3.27-orig/programs/pluto/crypto.c	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/programs/pluto/crypto.c	2019-02-15 16:32:28.987835416 -0500
@@ -33,11 +33,12 @@
 #include "state.h"
 #include "log.h"
 #include "crypto.h"
-#include "alg_info.h"
+#include "proposals.h"
 #include "ike_alg.h"
 #include "test_buffer.h"
 #include "connections.h"
 
+#include "ike_alg_integ.h"
 #include "kernel_alg.h"
 
 /*
@@ -47,7 +48,7 @@
  */
 void ike_alg_show_connection(const struct connection *c, const char *instance)
 {
-	if (c->alg_info_ike != NULL) {
+	if (c->ike_proposals.p != NULL) {
 		/*
 		 * List the algorithms as found in alg_info_ike and as
 		 * will be fed into the proposal code.
@@ -79,30 +80,46 @@
 		LSWLOG_WHACK(RC_COMMENT, buf) {
 			lswlogf(buf, "\"%s\"%s:   IKE algorithms: ",
 				c->name, instance);
-			lswlog_alg_info(buf, &c->alg_info_ike->ai);
+			fmt_proposals(buf, c->ike_proposals.p);
 		}
 	}
 
 	const struct state *st = state_with_serialno(c->newest_isakmp_sa);
 
 	if (st != NULL) {
-		/*
-		 * Convert the crypt-suite into 'struct proposal_info'
-		 * so that the parser's print-alg code can be used.
-		 */
-		const struct proposal_info p = {
-			.encrypt = st->st_oakley.ta_encrypt,
-			.enckeylen = st->st_oakley.enckeylen,
-			.prf = st->st_oakley.ta_prf,
-			.integ = st->st_oakley.ta_integ,
-			.dh = st->st_oakley.ta_dh,
-		};
-		const char *v = st->st_ikev2 ? "IKEv2" : "IKE";
 		LSWLOG_WHACK(RC_COMMENT, buf) {
 			lswlogf(buf,
 				"\"%s\"%s:   %s algorithm newest: ",
-				c->name, instance, v);
-			lswlog_proposal_info(buf, &p);
+				c->name, instance,
+				"IKE");
+			const struct trans_attrs *ta = &st->st_oakley;
+			const char *sep = "";
+			if (ta->ta_encrypt != NULL) {
+				lswlogs(buf, sep); sep = "-";
+				lswlogs(buf, ta->ta_encrypt->common.fqn);
+				if (ta->enckeylen != 0) {
+					lswlogf(buf, "_%d", ta->enckeylen);
+				}
+			}
+			if (ta->ta_prf != NULL) {
+				lswlogs(buf, sep); sep = "-";
+				lswlogs(buf, ta->ta_prf->common.fqn);
+			}
+			/* XXX: should just print everything */
+			if (ta->ta_integ != NULL) {
+				if ((ta->ta_prf == NULL) ||
+				    (encrypt_desc_is_aead(ta->ta_encrypt) &&
+				     ta->ta_integ != &ike_alg_integ_none) ||
+				    (!encrypt_desc_is_aead(ta->ta_encrypt) &&
+				     ta->ta_integ->prf != ta->ta_prf)) {
+					lswlogs(buf, sep); sep = "-";
+					lswlogs(buf, ta->ta_integ->common.fqn);
+				}
+			}
+			if (ta->ta_dh != NULL) {
+				lswlogs(buf, sep); sep = "-";
+				lswlogs(buf, ta->ta_dh->common.fqn);
+			}
 		}
 	}
 }
diff -Naur libreswan-3.27-orig/programs/pluto/db_ops.c libreswan-3.27/programs/pluto/db_ops.c
--- libreswan-3.27-orig/programs/pluto/db_ops.c	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/programs/pluto/db_ops.c	2019-02-15 16:32:28.987835416 -0500
@@ -62,6 +62,7 @@
 #include "defs.h"
 #include "state.h"
 #include "packet.h"
+#include "proposals.h"
 #include "spdb.h"
 #include "db_ops.h"
 #include "log.h"
diff -Naur libreswan-3.27-orig/programs/pluto/hmac.c libreswan-3.27/programs/pluto/hmac.c
--- libreswan-3.27-orig/programs/pluto/hmac.c	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/programs/pluto/hmac.c	2019-02-15 16:32:28.988835425 -0500
@@ -27,7 +27,7 @@
 #include "constants.h"
 #include "defs.h"
 #include "crypto.h"
-#include "alg_info.h"
+#include "proposals.h"
 #include "ike_alg.h"
 
 #include <nss.h>
diff -Naur libreswan-3.27-orig/programs/pluto/hostpair.c libreswan-3.27/programs/pluto/hostpair.c
--- libreswan-3.27-orig/programs/pluto/hostpair.c	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/programs/pluto/hostpair.c	2019-02-15 16:32:28.988835425 -0500
@@ -57,7 +57,7 @@
 #include "log.h"
 #include "keys.h"
 #include "whack.h"
-#include "alg_info.h"
+#include "proposals.h"
 #include "spdb.h"
 #include "ike_alg.h"
 #include "kernel_alg.h"
diff -Naur libreswan-3.27-orig/programs/pluto/ikev1_aggr.c libreswan-3.27/programs/pluto/ikev1_aggr.c
--- libreswan-3.27-orig/programs/pluto/ikev1_aggr.c	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/programs/pluto/ikev1_aggr.c	2019-02-15 16:32:28.989835435 -0500
@@ -24,7 +24,7 @@
 
 #include "constants.h"		/* for dup_any()!?! ... */
 #include "lswlog.h"
-#include "alg_info.h"
+#include "proposals.h"
 
 #include "defs.h"
 #include "state.h"
diff -Naur libreswan-3.27-orig/programs/pluto/ikev1.h libreswan-3.27/programs/pluto/ikev1.h
--- libreswan-3.27-orig/programs/pluto/ikev1.h	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/programs/pluto/ikev1.h	2019-02-15 16:32:28.989835435 -0500
@@ -6,11 +6,14 @@
 #include "packet.h"		/* for pb_stream */
 #include "fd.h"
 
+struct child_proposals;
+struct ike_proposals;
+
 /* ikev1.c */
 
 extern void init_ikev1(void);
 
-const struct oakley_group_desc *ikev1_quick_pfs(struct alg_info_esp *aie);
+const struct oakley_group_desc *ikev1_quick_pfs(const struct child_proposals proposals);
 
 void ikev1_init_out_pbs_echo_hdr(struct msg_digest *md, bool enc, uint8_t np,
 				 pb_stream *output_stream, uint8_t *output_buffer,
diff -Naur libreswan-3.27-orig/programs/pluto/ikev1_quick.c libreswan-3.27/programs/pluto/ikev1_quick.c
--- libreswan-3.27-orig/programs/pluto/ikev1_quick.c	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/programs/pluto/ikev1_quick.c	2019-02-15 16:32:28.990835444 -0500
@@ -79,20 +79,22 @@
 #include "virtual.h"	/* needs connections.h */
 #include "ikev1_dpd.h"
 #include "pluto_x509.h"
-#include "alg_info.h"
+#include "proposals.h"
 #include "ip_address.h"
 
 #include <blapit.h>
 
-const struct oakley_group_desc *ikev1_quick_pfs(struct alg_info_esp *aie)
+const struct oakley_group_desc *ikev1_quick_pfs(const struct child_proposals proposals)
 {
-	if (aie == NULL) {
+	if (proposals.p == NULL) {
 		return NULL;
 	}
-	if (aie->ai.alg_info_cnt == 0) {
+	struct proposal *proposal = next_proposal(proposals.p, NULL);
+	struct algorithm *dh = next_algorithm(proposal, PROPOSAL_dh, NULL);
+	if (dh == NULL) {
 		return NULL;
 	}
-	return aie->ai.proposals[0].dh;
+	return dh_desc(dh->desc);
 }
 
 /* accept_PFS_KE
@@ -816,7 +818,7 @@
 		 * use that group.
 		 * if not, fallback to old use-same-as-P1 behaviour
 		 */
-		st->st_pfs_group = ikev1_quick_pfs(c->alg_info_esp);
+		st->st_pfs_group = ikev1_quick_pfs(c->child_proposals);
 		/* otherwise, use the same group as during Phase 1:
 		 * since no negotiation is possible, we pick one that is
 		 * very likely supported.
@@ -832,8 +834,8 @@
 		}
 		lswlogf(buf, " {using isakmp#%lu msgid:%08" PRIx32 " proposal=",
 			isakmp_sa->st_serialno, st->st_msgid);
-		if (st->st_connection->alg_info_esp != NULL) {
-			lswlog_alg_info(buf, &st->st_connection->alg_info_esp->ai);
+		if (st->st_connection->child_proposals.p != NULL) {
+			fmt_proposals(buf, st->st_connection->child_proposals.p);
 		} else {
 			lswlogf(buf, "defaults");
 		}
diff -Naur libreswan-3.27-orig/programs/pluto/ikev1_spdb_struct.c libreswan-3.27/programs/pluto/ikev1_spdb_struct.c
--- libreswan-3.27-orig/programs/pluto/ikev1_spdb_struct.c	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/programs/pluto/ikev1_spdb_struct.c	2019-02-15 16:32:28.991835453 -0500
@@ -47,7 +47,7 @@
 #include "crypto.h"
 
 #include "ikev1.h"
-#include "alg_info.h"
+#include "proposals.h"
 #include "kernel_alg.h"
 #include "ike_alg.h"
 #include "ike_alg_encrypt.h"
@@ -305,17 +305,18 @@
 		return false;
 	}
 
-	if (c->alg_info_esp== NULL) {
+	if (c->child_proposals.p == NULL) {
 		DBGF(DBG_CONTROL, "ESP IPsec Transform verified unconditionally; no alg_info to check against");
 		return true;
 	}
 
-	FOR_EACH_ESP_INFO(c->alg_info_esp, esp_info) {
-		if (esp_info->encrypt == ta->ta_encrypt &&
-		    (esp_info->enckeylen == 0 ||
-		     ta->enckeylen == 0 ||
-		     esp_info->enckeylen == ta->enckeylen) &&
-		    esp_info->integ == ta->ta_integ) {
+	FOR_EACH_PROPOSAL(c->child_proposals.p, proposal) {
+		struct v1_proposal algs = v1_proposal(proposal);
+		if (algs.encrypt == ta->ta_encrypt &&
+			(algs.enckeylen == 0 ||
+			ta->enckeylen == 0 ||
+			algs.enckeylen == ta->enckeylen) &&
+			algs.integ == ta->ta_integ) {
 			DBG(DBG_CONTROL,
 			    DBG_log("ESP IPsec Transform verified; matches alg_info entry"));
 			return true;
@@ -351,14 +352,15 @@
 			    ta->ta_dh->common.fqn);
 		return false;
 	}
-	if (c->alg_info_esp == NULL) {
+	if (c->child_proposals.p == NULL) {
 		DBG(DBG_CONTROL,
 		    DBG_log("AH IPsec Transform verified unconditionally; no alg_info to check against"));
 		return true;
 	}
 
-	FOR_EACH_ESP_INFO(c->alg_info_esp, esp_info) {	/* really AH */
-		if (esp_info->integ == ta->ta_integ) {
+	FOR_EACH_PROPOSAL(c->child_proposals.p, proposal) {     /* really AH */
+		struct v1_proposal algs = v1_proposal(proposal);
+		if (algs.integ == ta->ta_integ) {
 			DBG(DBG_CONTROL,
 			    DBG_log("ESP IPsec Transform verified; matches alg_info entry"));
 			return true;
@@ -401,12 +403,12 @@
 		 * Aggr-Mode - Max transforms == 2 - Multiple
 		 * transforms, 1 DH group
 		 */
-		revised_sadb = oakley_alg_makedb(st->st_connection->alg_info_ike,
+		revised_sadb = oakley_alg_makedb(st->st_connection->ike_proposals,
 						 auth_method,
 						 aggressive_mode);
 	} else {
 		revised_sadb = kernel_alg_makedb(st->st_connection->policy,
-						 st->st_connection->alg_info_esp,
+						 st->st_connection->child_proposals,
 						 TRUE);
 
 		/* add IPcomp proposal if policy asks for it */
@@ -984,7 +986,7 @@
 }
 
 static bool ikev1_verify_ike(const struct trans_attrs *ta,
-			     struct alg_info_ike *alg_info_ike)
+			     struct ike_proposals ike_proposals)
 {
 	if (ta->ta_encrypt == NULL) {
 		loglog(RC_LOG_SERIOUS,
@@ -1005,7 +1007,7 @@
 		loglog(RC_LOG_SERIOUS, "OAKLEY proposal refused: missing DH");
 		return false;
 	}
-	if (alg_info_ike == NULL) {
+	if (ike_proposals.p == NULL) {
 		DBG(DBG_CONTROL,
 		    DBG_log("OAKLEY proposal verified unconditionally; no alg_info to check against"));
 		return true;
@@ -1016,14 +1018,14 @@
 	 * if specified in "esp" string
 	 */
 	bool ealg_insecure = (ta->enckeylen < 128);
-
-	FOR_EACH_IKE_INFO(alg_info_ike, ike_info) {
-		if (ike_info->encrypt == ta->ta_encrypt &&
-		    (ike_info->enckeylen == 0 ||
+	FOR_EACH_PROPOSAL(ike_proposals.p, proposal) {
+		struct v1_proposal algs = v1_proposal(proposal);
+		if (algs.encrypt == ta->ta_encrypt &&
+		    (algs.enckeylen == 0 ||
 		     ta->enckeylen == 0 ||
-		     ike_info->enckeylen == ta->enckeylen) &&
-		    ike_info->prf == ta->ta_prf &&
-		    ike_info->dh == ta->ta_dh) {
+		     algs.enckeylen == ta->enckeylen) &&
+		    algs.prf == ta->ta_prf &&
+		    algs.dh == ta->ta_dh) {
 			if (ealg_insecure) {
 				loglog(RC_LOG_SERIOUS,
 				       "You should NOT use insecure/broken IKE algorithms (%s)!",
@@ -1522,7 +1524,7 @@
 				}
 				/*
 				 * check if this keylen is compatible
-				 * with specified alg_info_ike.
+				 * with specified ike_proposals.
 				 */
 				if (!encrypt_has_key_bit_length(ta.ta_encrypt, val)) {
 					ugh = "peer proposed key_len not valid for encrypt algo setup specified";
@@ -1584,9 +1586,9 @@
 			}
 
 			/*
-			 * ML: at last check for allowed transforms in alg_info_ike
+			 * ML: at last check for allowed transforms in ike_proposals
 			 */
-			if (!ikev1_verify_ike(&ta, c->alg_info_ike)) {
+			if (!ikev1_verify_ike(&ta, c->ike_proposals)) {
 				/*
 				 * already logged; UGH acts as a skip
 				 * rest of checks flag
@@ -1715,7 +1717,7 @@
 	const struct connection *c = st->st_connection;
 
 	/*
-	 * Construct the proposals by combining ALG_INFO_IKE with the
+	 * Construct the proposals by combining IKE_PROPOSALS with the
 	 * AUTH (proof of identity) extracted from the aggressive mode
 	 * SADB.  As if by magic, attrs[2] is always the
 	 * authentication method.
@@ -1733,7 +1735,7 @@
 		 * Max transforms == 2 - Multiple transforms, 1 DH
 		 * group
 		 */
-		revised_sadb = oakley_alg_makedb(c->alg_info_ike,
+		revised_sadb = oakley_alg_makedb(c->ike_proposals,
 						 auth_method, TRUE);
 	}
 
diff -Naur libreswan-3.27-orig/programs/pluto/ikev2.c libreswan-3.27/programs/pluto/ikev2.c
--- libreswan-3.27-orig/programs/pluto/ikev2.c	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/programs/pluto/ikev2.c	2019-02-15 16:32:28.993835472 -0500
@@ -68,7 +68,7 @@
 #include "vendor.h"
 #include "ip_address.h"
 #include "ikev2_send.h"
-#include "alg_info.h" /* for ike_info / esp_info */
+#include "proposals.h" /* for ike_info / esp_info */
 
 #include "ietf_constants.h"
 
diff -Naur libreswan-3.27-orig/programs/pluto/ikev2_crypto.c libreswan-3.27/programs/pluto/ikev2_crypto.c
--- libreswan-3.27-orig/programs/pluto/ikev2_crypto.c	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/programs/pluto/ikev2_crypto.c	2019-02-15 16:32:28.993835472 -0500
@@ -52,7 +52,7 @@
 #include "ikev2.h"
 #include "ikev2_prf.h"
 #include "ike_alg.h"
-#include "alg_info.h"
+#include "proposals.h"
 #include "kernel_alg.h"
 #include "crypt_symkey.h"
 #include "ikev2_prf.h"
diff -Naur libreswan-3.27-orig/programs/pluto/ikev2.h libreswan-3.27/programs/pluto/ikev2.h
--- libreswan-3.27-orig/programs/pluto/ikev2.h	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/programs/pluto/ikev2.h	2019-02-15 16:32:28.994835481 -0500
@@ -97,11 +97,27 @@
 void free_ikev2_proposal(struct ikev2_proposal **proposal);
 void free_ikev2_proposals(struct ikev2_proposals **proposals);
 
-void ikev2_need_ike_proposals(struct connection *c, const char *why);
+/*
+ * On-demand, generate proposals for either the IKE SA or the CHILD
+ * SA.
+ *
+ * For CHILD SAs, two different proposal suites are used: during the
+ * IKE_AUTH exchange a stripped down proposal that excludes DH; and
+ * during the CREATE_CHILD_SA exchange DH a mashed up proposal that
+ * can include the IKE SA's latest DH.
+ *
+ * This is done on-demand as, only at the point where the IKE or CHILD
+ * SA is being instantiated, is it clear what proposals are needed.
+ * For instance, when a CHILD SA shares an existing IKE SA, the CHILD
+ * won't need IKE proposals but will need the IKE SA's DH.
+ *
+ * XXX: Should the CREATE CHILD SA proposals be stored in the state?
+ */
 
-void ikev2_need_esp_or_ah_proposals(struct connection *c,
-				    const char *why,
-				    const struct oakley_group_desc *default_dh);
+struct ikev2_proposals *get_v2_ike_proposals(struct connection *c, const char *why);
+struct ikev2_proposals *get_v2_ike_auth_child_proposals(struct connection *c, const char *why);
+struct ikev2_proposals *get_v2_create_child_proposals(struct connection *c, const char *why,
+						      const struct oakley_group_desc *default_dh);
 
 bool ikev2_emit_sa_proposal(pb_stream *pbs,
 			    const struct ikev2_proposal *proposal,
diff -Naur libreswan-3.27-orig/programs/pluto/ikev2_parent.c libreswan-3.27/programs/pluto/ikev2_parent.c
--- libreswan-3.27-orig/programs/pluto/ikev2_parent.c	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/programs/pluto/ikev2_parent.c	2019-02-15 16:32:28.996835500 -0500
@@ -67,7 +67,7 @@
 #include "pending.h"
 #include "kernel.h"
 #include "nat_traversal.h"
-#include "alg_info.h" /* for ike_info / esp_info */
+#include "proposals.h" /* for ike_info / esp_info */
 #include "key.h" /* for SECKEY_DestroyPublicKey */
 #include "vendor.h"
 #include "crypt_hash.h"
@@ -848,8 +848,9 @@
 	 * Initialize st->st_oakley, including the group number.
 	 * Grab the DH group from the first configured proposal and build KE.
 	 */
-	ikev2_need_ike_proposals(c, "IKE SA initiator selecting KE");
-	st->st_oakley.ta_dh = ikev2_proposals_first_dh(c->ike_proposals);
+	struct ikev2_proposals *ike_proposals =
+		get_v2_ike_proposals(c, "IKE SA initiator selecting KE");
+	st->st_oakley.ta_dh = ikev2_proposals_first_dh(ike_proposals);
 	if (st->st_oakley.ta_dh == NULL) {
 		libreswan_log("proposals do not contain a valid DH");
 		delete_state(st); /* pops state? */
@@ -991,7 +992,8 @@
 	}
 	/* SA out */
 	{
-		ikev2_need_ike_proposals(c, "IKE SA initiator emitting local proposals");
+		struct ikev2_proposals *ike_proposals =
+                get_v2_ike_proposals(c, "IKE SA initiator emitting local proposals");
 		/*
 		 * Since this is an initial IKE exchange, the SPI is
 		 * emitted as is part of the packet header and not the
@@ -999,7 +1001,7 @@
 		 */
 		u_char *sa_start = rbody.cur;
 		bool ret = ikev2_emit_sa_proposals(&rbody,
-						   c->ike_proposals,
+						   ike_proposals,
 						   (chunk_t*)NULL);
 		if (!ret) {
 			libreswan_log("outsa fail");
@@ -1328,7 +1330,8 @@
 	}
 
 	/* Get the proposals ready.  */
-	ikev2_need_ike_proposals(c, "IKE SA responder matching remote proposals");
+	struct ikev2_proposals *ike_proposals =
+		get_v2_ike_proposals(c, "IKE SA responder matching remote proposals");
 
 	/*
 	 * Select the proposal.
@@ -1342,7 +1345,7 @@
 						  /*expect_accepted*/ FALSE,
 						  LIN(POLICY_OPPORTUNISTIC, c->policy),
 						  &accepted_ike_proposal,
-						  c->ike_proposals);
+						  ike_proposals);
 	if (ret != STF_OK)
 		return ret;
 
@@ -1812,9 +1815,10 @@
 			pstats(invalidke_recv_s, sg.sg_group);
 			pstats(invalidke_recv_u, st->st_oakley.ta_dh->group);
 
-			ikev2_need_ike_proposals(c, "IKE SA initiator validating remote's suggested KE");
+			struct ikev2_proposals *ike_proposals =
+				get_v2_ike_proposals(c, "IKE SA initiator validating remote's suggested KE");
 
-			if (ikev2_proposals_include_modp(c->ike_proposals, sg.sg_group)) {
+			if (ikev2_proposals_include_modp(ike_proposals, sg.sg_group)) {
 				DBG(DBG_CONTROLMORE, DBG_log("Suggested modp group is acceptable"));
 				/*
 				 * Since there must be a group object
@@ -2081,7 +2085,9 @@
 		/* SA body in and out */
 		struct payload_digest *const sa_pd =
 			md->chain[ISAKMP_NEXT_v2SA];
-		ikev2_need_ike_proposals(c, "IKE SA initiator accepting remote proposal");
+		struct ikev2_proposals *ike_proposals =
+			get_v2_ike_proposals(c, "IKE SA initiator accepting remote proposal");
+
 
 		stf_status ret = ikev2_process_sa_payload("IKE initiator (accepting)",
 							  &sa_pd->pbs,
@@ -2090,7 +2096,7 @@
 							  /*expect_accepted*/ TRUE,
 							  LIN(POLICY_OPPORTUNISTIC, c->policy),
 							  &st->st_accepted_ike_proposal,
-							  c->ike_proposals);
+							  ike_proposals);
 		if (ret != STF_OK) {
 			DBG(DBG_CONTROLMORE, DBG_log("ikev2_parse_parent_sa_body() failed in ikev2_parent_inR1outI2()"));
 			return ret;
@@ -3564,17 +3570,13 @@
 			 sizeof(proto_info->our_spi));
 
 		/*
-		 * UNSET_GROUP means strip DH from the proposal. A
-		 * CHILD_SA established during an AUTH exchange does
+		 * A CHILD_SA established during an AUTH exchange does
 		 * not propose DH - the IKE SA's SKEYSEED is always
 		 * used.
 		 */
-		free_ikev2_proposals(&cc->esp_or_ah_proposals);
-		ikev2_need_esp_or_ah_proposals(cc,
-					       "IKE SA initiator emitting ESP/AH proposals",
-					       &unset_group);
-
-		if (!ikev2_emit_sa_proposals(&e_pbs_cipher, cc->esp_or_ah_proposals,
+		struct ikev2_proposals *child_proposals =
+			get_v2_ike_auth_child_proposals(cc, "IKE SA initiator emitting ESP/AH proposals");
+		if (!ikev2_emit_sa_proposals(&e_pbs_cipher, child_proposals,
 					     &local_spi))
 			return STF_INTERNAL_ERROR;
 
@@ -4374,31 +4376,25 @@
 	stf_status ret;
 
 	char *what;
-	const struct oakley_group_desc *default_dh;
+	struct ikev2_proposals *child_proposals;
 	if (isa_xchg == ISAKMP_v2_CREATE_CHILD_SA) {
 		if (st->st_state == STATE_V2_CREATE_I) {
 			what = "ESP/AH initiator accepting remote proposal";
 		} else {
 			what = "ESP/AH responder matching remote proposals";
 		}
-		default_dh = (c->policy & POLICY_PFS) != LEMPTY
+		const struct oakley_group_desc *default_dh = (c->policy & POLICY_PFS) != LEMPTY
 			? ike->sa.st_oakley.ta_dh
 			: &ike_alg_dh_none;
+		child_proposals = get_v2_create_child_proposals(c, what, default_dh);
 	} else if (expect_accepted) {
-		what = "IKE SA initiator accepting remote ESP/AH proposal";
-		default_dh = &unset_group; /* no DH */
+		what = "IKE_AUTH initiator accepting remote ESP/AH proposal";
+		child_proposals = get_v2_ike_auth_child_proposals(c, what);
 	} else {
-		what = "IKE SA responder matching remote ESP/AH proposals";
-		default_dh = &unset_group; /* no DH */
-	}
-
-	if (!expect_accepted) {
-		/* preparing to initiate or parse a request flush old ones */
-		free_ikev2_proposals(&c->esp_or_ah_proposals);
+		what = "IKE_AUTH responder matching remote ESP/AH proposals";
+		child_proposals = get_v2_ike_auth_child_proposals(c, what);
 	}
 
-	ikev2_need_esp_or_ah_proposals(c, what, default_dh);
-
 	ret = ikev2_process_sa_payload(what,
 			&sa_pd->pbs,
 			/*expect_ike*/ FALSE,
@@ -4406,7 +4402,7 @@
 			expect_accepted,
 			LIN(POLICY_OPPORTUNISTIC, c->policy),
 			&st->st_accepted_esp_or_ah_proposal,
-			c->esp_or_ah_proposals);
+			child_proposals);
 
 	if (ret != STF_OK) {
 		LSWLOG_RC(RC_LOG_SERIOUS, buf) {
@@ -5217,8 +5213,8 @@
 	chunk_t local_spi;
 	setchunk(local_spi, (uint8_t*)&proto_info->our_spi,
 			sizeof(proto_info->our_spi));
-
-	if (!ikev2_emit_sa_proposals(outpbs, cc->esp_or_ah_proposals,
+	passert(cc->v2_create_child_proposals != NULL);
+	if (!ikev2_emit_sa_proposals(outpbs, cc->v2_create_child_proposals,
 				     &local_spi))
 		return STF_INTERNAL_ERROR;
 
@@ -5312,12 +5308,11 @@
 				sizeof(st->st_icookie));
 		local_nonce = st->st_ni;
 
-		/* ??? why do we need to free the previous proposals? */
-		free_ikev2_proposals(&c->ike_proposals);
-		ikev2_need_ike_proposals(c, "IKE SA initiating rekey");
+		struct ikev2_proposals *ike_proposals =
+			get_v2_ike_proposals(c, "IKE SA initiating rekey");
 
 		/* send v2 IKE SAs*/
-		if (!ikev2_emit_sa_proposals(outpbs, c->ike_proposals, &local_spi))  {
+		if (!ikev2_emit_sa_proposals(outpbs, ike_proposals, &local_spi))  {
 			libreswan_log("outsa fail");
 			DBG(DBG_CONTROL, DBG_log("problem emitting connection ike proposals in CREATE_CHILD_SA"));
 			return STF_INTERNAL_ERROR;
@@ -5379,7 +5374,8 @@
 	struct payload_digest *const sa_pd = md->chain[ISAKMP_NEXT_v2SA];
 
 	/* Get the proposals ready.  */
-	ikev2_need_ike_proposals(c, "IKE SA accept response to rekey");
+	struct ikev2_proposals *ike_proposals =
+                get_v2_ike_proposals(c, "IKE SA accept response to rekey");
 
 	stf_status ret = ikev2_process_sa_payload("IKE initiator (accepting)",
 						  &sa_pd->pbs,
@@ -5388,7 +5384,7 @@
 						  /*expect_accepted*/ TRUE,
 						  LIN(POLICY_OPPORTUNISTIC, c->policy),
 						  &st->st_accepted_ike_proposal,
-						  c->ike_proposals);
+						  ike_proposals);
 	if (ret != STF_OK) {
 		DBG(DBG_CONTROLMORE, DBG_log("failed to accept IKE SA, REKEY, response, in process_ike_rekey_sa_pl_response"));
 		return ret;
@@ -5424,7 +5420,8 @@
 	struct payload_digest *const sa_pd = md->chain[ISAKMP_NEXT_v2SA];
 
 	/* Get the proposals ready.  */
-	ikev2_need_ike_proposals(c, "IKE SA responding to rekey");
+	struct ikev2_proposals *ike_proposals =
+		get_v2_ike_proposals(c, "IKE SA responding to rekey");
 
 	struct ikev2_proposal *accepted_ike_proposal = NULL;
 	stf_status ret = ikev2_process_sa_payload("IKE Rekey responder child",
@@ -5434,7 +5431,7 @@
 			/*expect_accepted*/ FALSE,
 			LIN(POLICY_OPPORTUNISTIC, c->policy),
 			&accepted_ike_proposal,
-			c->ike_proposals);
+			ike_proposals);
 	if (ret != STF_OK)
 		return ret;
 
@@ -6677,21 +6674,17 @@
 	if (sa_type == IPSEC_SA) {
 		const struct state *rst = state_with_serialno(p->replacing);
 
-		/*
-		 * Because the proposal generated during AUTH won't contain DH,
-		 * always force the proposal to be re-generated here.  Not the
-		 * most efficient, fix probably means moving the proposals to
-		 * the state object.
-		 */
-		free_ikev2_proposals(&c->esp_or_ah_proposals);
 		const struct oakley_group_desc *default_dh =
 			c->policy & POLICY_PFS ? ike->sa.st_oakley.ta_dh : NULL;
 
-		ikev2_need_esp_or_ah_proposals(c,
-					       "ESP/AH initiator emitting proposals",
-					       default_dh);
+		struct ikev2_proposals *child_proposals =
+			get_v2_create_child_proposals(c,
+							"ESP/AH initiator emitting proposals",
+							default_dh);
+		/* see ikev2_child_add_ipsec_payloads */
+		passert(c->v2_create_child_proposals != NULL);
 
-		st->st_pfs_group = ikev2_proposals_first_dh(c->esp_or_ah_proposals);
+		st->st_pfs_group = ikev2_proposals_first_dh(child_proposals);
 
 		DBG(DBG_CONTROLMORE, {
 			const char *pfsgroupname = st->st_pfs_group == NULL ?
diff -Naur libreswan-3.27-orig/programs/pluto/ikev2_spdb_struct.c libreswan-3.27/programs/pluto/ikev2_spdb_struct.c
--- libreswan-3.27-orig/programs/pluto/ikev2_spdb_struct.c	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/programs/pluto/ikev2_spdb_struct.c	2019-02-15 16:32:28.998835518 -0500
@@ -51,7 +51,7 @@
 
 #include "crypto.h"
 
-#include "alg_info.h"
+#include "proposals.h"
 #include "kernel_alg.h"
 #include "ike_alg.h"
 #include "ike_alg_integ.h"
@@ -264,7 +264,7 @@
  *
  * PROPNUM is an int.
  */
-#define FOR_EACH_PROPOSAL(PROPNUM, PROPOSAL, PROPOSALS)			\
+#define FOR_EACH_V2_PROPOSAL(PROPNUM, PROPOSAL, PROPOSALS)		\
 	for ((PROPNUM) = 1,						\
 		     (PROPOSAL) = &(PROPOSALS)->proposal[(PROPNUM)];	\
 	     (PROPNUM) < (PROPOSALS)->roof;				\
@@ -276,7 +276,7 @@
  *
  * PROPNUM, BASE, BOUND are all ints.
  */
-#define FOR_EACH_PROPOSAL_IN_RANGE(PROPNUM, PROPOSAL, PROPOSALS, BASE, BOUND) \
+#define FOR_EACH_V2_PROPOSAL_IN_RANGE(PROPNUM, PROPOSAL, PROPOSALS, BASE, BOUND) \
 	for ((PROPNUM) = ((BASE) > 0 ? (BASE) : 1),			\
 		     (PROPOSAL) = &(PROPOSALS)->proposal[(PROPNUM)];	\
 	     (PROPNUM) < (BOUND) && (PROPNUM) < (PROPOSALS)->roof;	\
@@ -395,7 +395,7 @@
 	const char *proposal_sep = "";
 	int propnum;
 	const struct ikev2_proposal *proposal;
-	FOR_EACH_PROPOSAL(propnum, proposal, proposals) {
+	FOR_EACH_V2_PROPOSAL(propnum, proposal, proposals) {
 		lswlogs(buf, proposal_sep);
 		proposal_sep = " ";
 		print_proposal(buf, propnum, proposal);
@@ -443,8 +443,8 @@
 	{
 		int local_propnum;
 		const struct ikev2_proposal *local_proposal;
-		FOR_EACH_PROPOSAL_IN_RANGE(local_propnum, local_proposal, local_proposals,
-					   local_propnum_base, local_propnum_bound) {
+		FOR_EACH_V2_PROPOSAL_IN_RANGE(local_propnum, local_proposal, local_proposals,
+					      local_propnum_base, local_propnum_bound) {
 			struct ikev2_proposal_match *matching_local_proposal = &matching_local_proposals[local_propnum];
 			/* clear matched */
 			matching_local_proposal->matched_transform_types = LEMPTY;
@@ -579,8 +579,8 @@
 		 */
 		int local_propnum;
 		struct ikev2_proposal *local_proposal;
-		FOR_EACH_PROPOSAL_IN_RANGE(local_propnum, local_proposal, local_proposals,
-					   local_propnum_base, local_propnum_bound) {
+		FOR_EACH_V2_PROPOSAL_IN_RANGE(local_propnum, local_proposal, local_proposals,
+					      local_propnum_base, local_propnum_bound) {
 			if (local_proposal->protoid == remote_protoid) {
 				/*
 				 * Search the proposal for transforms of this
@@ -680,8 +680,8 @@
 
 	int local_propnum;
 	struct ikev2_proposal *local_proposal;
-	FOR_EACH_PROPOSAL_IN_RANGE(local_propnum, local_proposal, local_proposals,
-				   local_propnum_base, local_propnum_bound) {
+	FOR_EACH_V2_PROPOSAL_IN_RANGE(local_propnum, local_proposal, local_proposals,
+				      local_propnum_base, local_propnum_bound) {
 		struct ikev2_proposal_match *matching_local_proposal = &matching_local_proposals[local_propnum];
 		LSWDBGP(DBG_CONTROLMORE, log) {
 			lswlogf(log, "comparing remote proposal %u containing ",
@@ -834,7 +834,7 @@
 	{
 		int local_propnum;
 		struct ikev2_proposal *local_proposal;
-		FOR_EACH_PROPOSAL(local_propnum, local_proposal, local_proposals) {
+		FOR_EACH_V2_PROPOSAL(local_propnum, local_proposal, local_proposals) {
 			struct ikev2_proposal_match *matching_local_proposal = &matching_local_proposals[local_propnum];
 			enum ikev2_trans_type type;
 			struct ikev2_transforms *local_transforms;
@@ -1156,7 +1156,7 @@
 	 *
 	 * Must be freed by this function.
 	 */
-	stf_status status;
+	stf_status status = STF_FAIL;	/* initialized just to silence gcc -Og */
 	LSWBUF(remote_print_buf) {
 		int matching_local_propnum = ikev2_process_proposals(sa_payload,
 								     expect_ike, expect_spi,
@@ -1448,7 +1448,7 @@
 
 	int propnum;
 	const struct ikev2_proposal *proposal;
-	FOR_EACH_PROPOSAL(propnum, proposal, proposals) {
+	FOR_EACH_V2_PROPOSAL(propnum, proposal, proposals) {
 		if (!emit_proposal(&sa_pbs, proposal, propnum, local_spi,
 				   (propnum < proposals->roof - 1
 				    ? v2_PROPOSAL_NON_LAST
@@ -1804,9 +1804,9 @@
 	return TRUE;
 }
 
-static struct ikev2_proposal *ikev2_proposal_from_proposal_info(const struct proposal_info *info,
+static struct ikev2_proposal *ikev2_proposal_from_proposal_info(const struct proposal *proposal,
 								enum ikev2_sec_proto_id protoid,
-								struct ikev2_proposals *proposals,
+								struct ikev2_proposals *v2_proposals,
 								const struct oakley_group_desc *default_dh)
 {
 	/*
@@ -1814,19 +1814,19 @@
 	 * contain partially constructed stuff from an earlier
 	 * iteration).
 	 */
-	struct ikev2_proposal *proposal = &proposals->proposal[proposals->roof];
-	*proposal = (struct ikev2_proposal) {
+	struct ikev2_proposal *v2_proposal = &v2_proposals->proposal[v2_proposals->roof];
+	*v2_proposal = (struct ikev2_proposal) {
 		.protoid = protoid,
-		.propnum = proposals->roof,
+		.propnum = v2_proposals->roof,
 	};
 
 	/*
 	 * Encryption.
 	 */
-	const struct encrypt_desc *encrypt = info->encrypt;
-	if (encrypt != NULL) {
-		if (!append_encrypt_transform(proposal, encrypt,
-					      info->enckeylen)) {
+	FOR_EACH_ALGORITHM(proposal, encrypt, alg) {
+		const struct encrypt_desc *encrypt = encrypt_desc(alg->desc);
+		if (!append_encrypt_transform(v2_proposal, encrypt,
+					      alg->enckeylen)) {
 			return NULL;
 		}
 	}
@@ -1834,22 +1834,22 @@
 	/*
 	 * PRF.
 	 */
-	const struct prf_desc *prf = info->prf;
-	if (prf != NULL) {
-		append_transform(proposal, IKEv2_TRANS_TYPE_PRF,
+	FOR_EACH_ALGORITHM(proposal, prf, alg) {
+		const struct prf_desc *prf = prf_desc(alg->desc);
+		append_transform(v2_proposal, IKEv2_TRANS_TYPE_PRF,
 				 prf->common.id[IKEv2_ALG_ID], 0);
 	}
 
 	/*
 	 * Integrity.
 	 */
-	const struct integ_desc *integ = info->integ;
-	if (integ != NULL) {
+	FOR_EACH_ALGORITHM(proposal, integ, alg) {
+		const struct integ_desc *integ = integ_desc(alg->desc);
 		/*
 		 * While INTEG=NONE is included in the proposal it
 		 * omitted when emitted.
 		 */
-		append_transform(proposal, IKEv2_TRANS_TYPE_INTEG,
+		append_transform(v2_proposal, IKEv2_TRANS_TYPE_INTEG,
 				 integ->common.id[IKEv2_ALG_ID], 0);
 	}
 
@@ -1860,20 +1860,25 @@
 	 * happens during the AUTH exchange).  Otherwise use either
 	 * the proposed or default DH.
 	 */
-	const struct oakley_group_desc *dh =
-		default_dh == &unset_group ? &ike_alg_dh_none
-		: info->dh != NULL ? info->dh
-		: default_dh;
-	if (dh != NULL) {
-		/*
-		 * WHILE DH=NONE is included in the proposal it is
-		 * omitted when emitted.
-		 */
-		append_transform(proposal, IKEv2_TRANS_TYPE_DH,
-				 dh->common.id[IKEv2_ALG_ID], 0);
+	if (default_dh == &unset_group) {
+		append_transform(v2_proposal, IKEv2_TRANS_TYPE_DH,
+				 ike_alg_dh_none.common.id[IKEv2_ALG_ID], 0);
+	} else if (next_algorithm(proposal, PROPOSAL_dh, NULL) != NULL) {
+		FOR_EACH_ALGORITHM(proposal, dh, alg) {
+			const struct oakley_group_desc *dh = dh_desc(alg->desc);
+			/*
+			 * WHILE DH=NONE is included in the proposal it is
+			 * omitted when emitted.
+			 */
+			append_transform(v2_proposal, IKEv2_TRANS_TYPE_DH,
+					 dh->common.id[IKEv2_ALG_ID], 0);
+		}
+	} else if (default_dh != NULL) {
+		append_transform(v2_proposal, IKEv2_TRANS_TYPE_DH,
+				 default_dh->common.id[IKEv2_ALG_ID], 0);
 	}
 
-	return proposal;
+	return v2_proposal;
 }
 
 /*
@@ -1993,67 +1998,73 @@
 };
 
 /*
- * Ensure c->ike_proposals is filled in.  If not, build it.
- *
- * ??? if c->ike_proposals was set for v1 we won't change it.
+ * On-demand compute and return the IKE proposals for the connection.
  *
- * WARNING: alg_info_ike is IKEv1
+ * If the default alg_info_ike includes unknown algorithms those get
+ * dropped, which can lead to no proposals.
  *
- * If alg_info_ike includes unknown algorithms those get dropped,
- * which can lead to no proposals.
- * c->ike_proposals will not be NULL (see passert).
+ * Never returns NULL (see passert).
  */
 
-void ikev2_need_ike_proposals(struct connection *c, const char *why) {
-	if (c->ike_proposals != NULL) {
-		DBGF(DBG_CONTROL, "already constructed local IKE proposals for connection %s (%s)",
-		     c->name, why);
-		return;
+struct ikev2_proposals *get_v2_ike_proposals(struct connection *c, const char *why)
+{
+	if (c->v2_ike_proposals != NULL) {
+		LSWDBGP(DBG_CONTROL, buf) {
+			lswlogf(buf, "using existing local IKE proposals for connection %s (%s): ",
+				c->name, why);
+			print_proposals(buf, c->v2_ike_proposals);
+		}
+		return c->v2_ike_proposals;
 	}
 
 	const char *notes;
-	if (c->alg_info_ike == NULL) {
+	if (c->ike_proposals.p == NULL) {
 		DBGF(DBG_CONTROL, "selecting default constructed local IKE proposals for connection %s (%s)",
 		     c->name, why);
-		c->ike_proposals = &default_ikev2_ike_proposals;
+		c->v2_ike_proposals = &default_ikev2_ike_proposals;
 		notes = " (default)";
 	} else {
 		DBGF(DBG_CONTROL, "constructing local IKE proposals for %s (%s)",
 		     c->name, why);
-		struct ikev2_proposals *proposals = alloc_thing(struct ikev2_proposals, "proposals");
-		int proposals_roof = c->alg_info_ike->ai.alg_info_cnt + 1;
-		proposals->proposal = alloc_things(struct ikev2_proposal, proposals_roof, "propsal");
-		proposals->on_heap = TRUE;
-		proposals->roof = 1;
+		struct ikev2_proposals *v2_proposals = alloc_thing(struct ikev2_proposals,
+								   "proposals");
+		/* +1 as proposal[0] is empty */
+		int v2_proposals_roof = nr_proposals(c->ike_proposals.p) + 1;
+		v2_proposals->proposal = alloc_things(struct ikev2_proposal,
+						      v2_proposals_roof, "propsal");
+		v2_proposals->on_heap = TRUE;
+		v2_proposals->roof = 1;
 
-		FOR_EACH_IKE_INFO(c->alg_info_ike, ike_info) {
+		FOR_EACH_PROPOSAL(c->ike_proposals.p, ike_info) {
 			LSWDBGP(DBG_CONTROL, buf) {
 				lswlogs(buf, "converting ike_info ");
-				lswlog_proposal_info(buf, ike_info);
+				fmt_proposal(buf, ike_info);
 				lswlogs(buf, " to ikev2 ...");
 			}
 
-			passert(proposals->roof < proposals_roof);
-			struct ikev2_proposal *proposal =
-				ikev2_proposal_from_proposal_info(ike_info, IKEv2_SEC_PROTO_IKE,
-								  proposals, NULL);
-			if (proposal != NULL) {
+			passert(v2_proposals->roof < v2_proposals_roof);
+			struct ikev2_proposal *v2_proposal =
+				ikev2_proposal_from_proposal_info(ike_info,
+								  IKEv2_SEC_PROTO_IKE,
+								  v2_proposals, NULL);
+			if (v2_proposal != NULL) {
 				DBG(DBG_CONTROL,
-				    DBG_log_ikev2_proposal("... ", proposal));
-				proposals->roof++;
+				    DBG_log_ikev2_proposal("... ", v2_proposal));
+				v2_proposals->roof++;
 			}
 		}
-		c->ike_proposals = proposals;
+		c->v2_ike_proposals = v2_proposals;
 		notes = "";
 	}
 
-	LSWLOG(buf) {
+	LSWLOG_CONNECTION(c, buf) {
 		lswlogf(buf, "constructed local IKE proposals for %s (%s): ",
 			c->name, why);
-		print_proposals(buf, c->ike_proposals);
+		print_proposals(buf, c->v2_ike_proposals);
 		lswlogs(buf, notes);
 	}
-	passert(c->ike_proposals != NULL);
+	passert(c->v2_ike_proposals != NULL);
+	return c->v2_ike_proposals;
 }
 
 static struct ikev2_proposal default_ikev2_esp_proposal_missing_esn[] = {
@@ -2143,19 +2154,23 @@
 	}
 }
 
-void ikev2_need_esp_or_ah_proposals(struct connection *c,
-				    const char *why,
-				    const struct oakley_group_desc *default_dh)
+static struct ikev2_proposals *get_v2_child_proposals(struct ikev2_proposals **child_proposals,
+						      struct connection *c,
+						      const char *why,
+						      const struct oakley_group_desc *default_dh)
 {
-	if (c->esp_or_ah_proposals != NULL) {
-		DBGF(DBG_CONTROL, "already constructed local ESP/AH proposals for %s (%s)",
-		     c->name, why);
-		return;
+	if (*child_proposals != NULL) {
+		LSWDBGP(DBG_CONTROL, buf) {
+			lswlogf(buf, "using existing local ESP/AH proposals for %s (%s): ",
+				c->name, why);
+			print_proposals(buf, *child_proposals);
+		}
+		return *child_proposals;
 	}
 
 	const char *notes;
-	if (c->alg_info_esp == NULL) {
-		DBGF(DBG_CONTROL, "selecting default construvted local ESP/AH proposals for %s (%s)",
+	if (c->child_proposals.p == NULL) {
+		DBGF(DBG_CONTROL, "selecting default local ESP/AH proposals for %s (%s)",
 		     c->name, why);
 		lset_t esp_ah = c->policy & (POLICY_ENCRYPT | POLICY_AUTHENTICATE);
 		struct ikev2_proposals *default_proposals_missing_esn;
@@ -2186,38 +2201,38 @@
 		/*
 		 * Clone the default proposal and add the missing ESN.
 		 */
-		struct ikev2_proposals *proposals = alloc_thing(struct ikev2_proposals,
-								"cloned ESP/AH proposals");
-		proposals->on_heap = TRUE;
-		proposals->roof = default_proposals_missing_esn->roof;
+		struct ikev2_proposals *v2_proposals = alloc_thing(struct ikev2_proposals,
+								   "cloned ESP/AH proposals");
+		v2_proposals->on_heap = TRUE;
+		v2_proposals->roof = default_proposals_missing_esn->roof;
 		if (add_empty_msdh_duplicates) {
 			/* add space for duplicates, minus the empty first proposal */
-			proposals->roof += default_proposals_missing_esn->roof - 1;
+			v2_proposals->roof += default_proposals_missing_esn->roof - 1;
 		}
-		proposals->proposal = alloc_things(struct ikev2_proposal, proposals->roof,
-						   "ESP/AH proposals");
-		memcpy(proposals->proposal, default_proposals_missing_esn->proposal,
-		       sizeof(proposals->proposal[0]) * default_proposals_missing_esn->roof);
+		v2_proposals->proposal = alloc_things(struct ikev2_proposal, v2_proposals->roof,
+						      "ESP/AH proposals");
+		memcpy(v2_proposals->proposal, default_proposals_missing_esn->proposal,
+		       sizeof(v2_proposals->proposal[0]) * default_proposals_missing_esn->roof);
 		if (add_empty_msdh_duplicates) {
 			/*
 			 * Append duplicates but discarding
 			 * proposal[0] which is filler.
 			 */
-			memcpy(proposals->proposal + default_proposals_missing_esn->roof,
+			memcpy(v2_proposals->proposal + default_proposals_missing_esn->roof,
 			       default_proposals_missing_esn->proposal + 1, /* skip "0" */
-			       sizeof(proposals->proposal[0]) * (default_proposals_missing_esn->roof - 1));
+			       sizeof(v2_proposals->proposal[0]) * (default_proposals_missing_esn->roof - 1));
 		}
 
 		int propnum;
-		struct ikev2_proposal *proposal;
-		FOR_EACH_PROPOSAL(propnum, proposal, proposals) {
-			add_esn_transforms(proposal, c->policy);
+		struct ikev2_proposal *v2_proposal;
+		FOR_EACH_V2_PROPOSAL(propnum, v2_proposal, v2_proposals) {
+			add_esn_transforms(v2_proposal, c->policy);
 		}
 		if (default_dh != NULL && default_dh != &unset_group) {
 			DBGF(DBG_CONTROL, "adding dh %s to default proposals",
 			     default_dh->common.name);
-			FOR_EACH_PROPOSAL(propnum, proposal, proposals) {
-				append_transform(proposal,
+			FOR_EACH_V2_PROPOSAL(propnum, v2_proposal, v2_proposals) {
+				append_transform(v2_proposal,
 						 IKEv2_TRANS_TYPE_DH,
 						 default_dh->group, 0);
 				if (propnum >= default_proposals_missing_esn->roof)
@@ -2225,7 +2240,7 @@
 					break;
 			}
 		}
-		c->esp_or_ah_proposals = proposals;
+		*child_proposals = v2_proposals;
 		notes = " (default)";
 	} else {
 		LSWDBGP(DBG_CONTROL, buf) {
@@ -2250,17 +2265,18 @@
 		bool add_empty_msdh_duplicates = (c->policy & POLICY_MSDH_DOWNGRADE) &&
 			default_dh != &unset_group;
 
-		struct ikev2_proposals *proposals = alloc_thing(struct ikev2_proposals,
-								"ESP/AH proposals");
-		int proposals_roof = c->alg_info_esp->ai.alg_info_cnt + 1;
+		struct ikev2_proposals *v2_proposals = alloc_thing(struct ikev2_proposals,
+								   "ESP/AH proposals");
+		/* proposal[0] is empty so +1 */
+		int v2_proposals_roof = nr_proposals(c->child_proposals.p) + 1;
 		if (add_empty_msdh_duplicates) {
-			/* make space for everything duplicated */
-			proposals_roof += c->alg_info_esp->ai.alg_info_cnt;
+			/* make space for everything duplicated; note +1 above */
+			v2_proposals_roof = v2_proposals_roof * 2 - 1;
 		}
-		proposals->proposal = alloc_things(struct ikev2_proposal, proposals_roof,
-						   "ESP/AH proposal");
-		proposals->on_heap = TRUE;
-		proposals->roof = 1;
+		v2_proposals->proposal = alloc_things(struct ikev2_proposal, v2_proposals_roof,
+						      "ESP/AH proposal");
+		v2_proposals->on_heap = TRUE;
+		v2_proposals->roof = 1;
 
 		enum ikev2_sec_proto_id protoid;
 		switch (c->policy & (POLICY_ENCRYPT | POLICY_AUTHENTICATE)) {
@@ -2275,10 +2291,10 @@
 		}
 
 		for (int dup = 0; dup < (add_empty_msdh_duplicates ? 2 : 1); dup++) {
-			FOR_EACH_ESP_INFO(c->alg_info_esp, esp_info) {
+			FOR_EACH_PROPOSAL(c->child_proposals.p, esp_info) {
 				LSWDBGP(DBG_CONTROL, log) {
 					lswlogf(log, "converting proposal ");
-					lswlog_proposal_info(log, esp_info);
+					fmt_proposal(log, esp_info);
 					lswlogf(log, " to ikev2 ...");
 				}
 
@@ -2286,37 +2302,94 @@
 				 * Get the next proposal with the
 				 * basics filled in.
 				 */
-				passert(proposals->roof < proposals_roof);
-				if (dup && default_dh == NULL && esp_info->dh == NULL) {
+				passert(v2_proposals->roof < v2_proposals_roof);
+				if (dup && default_dh == NULL &&
+				    next_algorithm(esp_info, PROPOSAL_dh, NULL) == NULL) {
 					/*
 					 * First pass didn't include DH.
 					 */
 					continue;
 				}
-				struct ikev2_proposal *proposal =
-					ikev2_proposal_from_proposal_info(esp_info, protoid,
-									  proposals,
+				struct ikev2_proposal *v2_proposal =
+					ikev2_proposal_from_proposal_info(esp_info,
+									  protoid,
+									  v2_proposals,
 									  dup ? &unset_group : default_dh);
-				if (proposal != NULL) {
-					add_esn_transforms(proposal, c->policy);
+				if (v2_proposal != NULL) {
+					add_esn_transforms(v2_proposal, c->policy);
 					DBG(DBG_CONTROL,
-					    DBG_log_ikev2_proposal("... ", proposal));
-					proposals->roof++;
+					    DBG_log_ikev2_proposal("... ", v2_proposal));
+					v2_proposals->roof++;
 				}
 			}
 		}
 
-		c->esp_or_ah_proposals = proposals;
+		*child_proposals = v2_proposals;
 		notes = "";
 	}
 
-	LSWLOG(buf) {
+	LSWLOG_CONNECTION(c, buf) {
 		lswlogf(buf, "constructed local ESP/AH proposals for %s (%s): ",
 			c->name, why);
-		print_proposals(buf, c->esp_or_ah_proposals);
+		print_proposals(buf, *child_proposals);
 		lswlogs(buf, notes);
 	}
-	passert(c->esp_or_ah_proposals != NULL);
+	passert(*child_proposals != NULL);
+	return *child_proposals;
+}
+
+/*
+ * If needed, generate the proposals for a CHILD SA being created
+ * during the IKE_AUTH exchange.
+ *
+ * Since a CHILD_SA established during an IKE_AUTH exchange does not
+ * propose DH (keying material is taken from the IKE SA's SKEYSEED),
+ * DH is stripped from the proposals.
+ *
+ * Since only things that affect this proposal suite are the
+ * connection's .policy bits and the contents .child_proposals, and
+ * modifiying those triggers the creation of a new connection (true?),
+ * the connection can be cached.
+ */
+
+struct ikev2_proposals *get_v2_ike_auth_child_proposals(struct connection *c, const char *why)
+{
+	/* UNSET_GROUP means strip DH from the proposal. */
+	return get_v2_child_proposals(&c->v2_ike_auth_child_proposals, c,
+				      why, &unset_group);
+}
+
+/*
+ * If needed, generate the proposals for a CHILD SA being created (or
+ * re-keyed) during a CREATE_CHILD_SA exchange.
+ *
+ * If the proposals do not include DH, and PFS is enabled, then the
+ * DEFAULT_DH (DH used by the IKE SA) is added to all proposals.
+ *
+ * XXX:
+ *
+ * This means that the CHILD SA's proposal suite will change depending
+ * on what DH is negotiated by the IKE SA!  Hence the need to save the
+ * DEFAULT_DH and check for change.  It should probably be storing the
+ * proposal in the state.
+ *
+ * Horrible.
+ */
+struct ikev2_proposals *get_v2_create_child_proposals(struct connection *c, const char *why,
+						      const struct oakley_group_desc *default_dh)
+{
+	if (c->v2_create_child_proposals_default_dh != default_dh) {
+		const char *old_fqn = (c->v2_create_child_proposals_default_dh != NULL
+				       ? c->v2_create_child_proposals_default_dh->common.fqn
+				       : "no-PFS");
+		const char *new_fqn = default_dh != NULL ? default_dh->common.fqn : "no-PFS";
+		DBGF(DBG_CONTROL, "create child proposal's DH changed from %s to %s, flushing",
+		    old_fqn, new_fqn);
+		free_ikev2_proposals(&c->v2_create_child_proposals);
+		c->v2_create_child_proposals_default_dh = default_dh;
+	}
+	return get_v2_child_proposals(&c->v2_create_child_proposals, c, why,
+				      c->v2_create_child_proposals_default_dh);
 }
 
 struct ipsec_proto_info *ikev2_child_sa_proto_info(struct state *st, lset_t policy)
@@ -2358,7 +2431,7 @@
 {
 	int propnum;
 	const struct ikev2_proposal *proposal;
-	FOR_EACH_PROPOSAL(propnum, proposal, proposals) {
+	FOR_EACH_V2_PROPOSAL(propnum, proposal, proposals) {
 		const struct ikev2_transforms *transforms = &proposal->transforms[IKEv2_TRANS_TYPE_DH];
 		int t;
 		for (t = 0; t < transforms->transform[t].valid; t++) {
@@ -2393,7 +2466,7 @@
 {
 	int propnum;
 	const struct ikev2_proposal *proposal;
-	FOR_EACH_PROPOSAL(propnum, proposal, proposals) {
+	FOR_EACH_V2_PROPOSAL(propnum, proposal, proposals) {
 		const struct ikev2_transforms *transforms = &proposal->transforms[IKEv2_TRANS_TYPE_DH];
 		const struct ikev2_transform *transform;
 		FOR_EACH_TRANSFORM(transform, transforms) {
diff -Naur libreswan-3.27-orig/programs/pluto/initiate.c libreswan-3.27/programs/pluto/initiate.c
--- libreswan-3.27-orig/programs/pluto/initiate.c	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/programs/pluto/initiate.c	2019-02-15 16:32:28.999835528 -0500
@@ -63,7 +63,7 @@
 #include "log.h"
 #include "keys.h"
 #include "whack.h"
-#include "alg_info.h"
+#include "proposals.h"
 #include "spdb.h"
 #include "ike_alg.h"
 #include "kernel_alg.h"
@@ -287,15 +287,12 @@
 	/* We will only request an IPsec SA if policy isn't empty
 	 * (ignoring Main Mode items).
 	 * This is a fudge, but not yet important.
-	 * If we are to proceed asynchronously, whackfd will be NULL_FD.
 	 */
 
 	if (c->policy & (POLICY_ENCRYPT | POLICY_AUTHENTICATE)) {
-		struct alg_info_esp *alg = c->alg_info_esp;
-		struct db_sa *phase2_sa = kernel_alg_makedb(
-			c->policy, alg, TRUE);
-
-		if (alg != NULL && phase2_sa == NULL) {
+		struct db_sa *phase2_sa =
+			kernel_alg_makedb(c->policy, c->child_proposals, TRUE);
+		if (c->child_proposals.p != NULL && phase2_sa == NULL) {
 			whack_log(RC_LOG_SERIOUS,
 				  "cannot initiate: no acceptable kernel algorithms loaded");
 			reset_cur_connection();
diff -Naur libreswan-3.27-orig/programs/pluto/kernel_bsdkame.c libreswan-3.27/programs/pluto/kernel_bsdkame.c
--- libreswan-3.27-orig/programs/pluto/kernel_bsdkame.c	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/programs/pluto/kernel_bsdkame.c	2019-02-15 16:32:28.999835528 -0500
@@ -47,7 +47,6 @@
 #include "whack.h"      /* for RC_LOG_SERIOUS */
 #include "packet.h"     /* for pb_stream in nat_traversal.h */
 #include "nat_traversal.h"
-#include "alg_info.h"
 #include "kernel_alg.h"
 #include "kernel_sadb.h"
 
diff -Naur libreswan-3.27-orig/programs/pluto/kernel.c libreswan-3.27/programs/pluto/kernel.c
--- libreswan-3.27-orig/programs/pluto/kernel.c	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/programs/pluto/kernel.c	2019-02-15 16:32:29.000835537 -0500
@@ -1176,7 +1176,6 @@
 	}
 }
 
-#include "alg_info.h"
 #include "kernel_alg.h"
 
 void set_text_said(char *text_said, const ip_address *dst,
diff -Naur libreswan-3.27-orig/programs/pluto/kernel_klips.c libreswan-3.27/programs/pluto/kernel_klips.c
--- libreswan-3.27-orig/programs/pluto/kernel_klips.c	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/programs/pluto/kernel_klips.c	2019-02-15 16:32:29.000835537 -0500
@@ -48,7 +48,6 @@
 #include "nat_traversal.h"
 #include "server.h"
 
-#include "alg_info.h"
 #include "kernel_alg.h"
 #include "ip_address.h"
 
diff -Naur libreswan-3.27-orig/programs/pluto/kernel_mast.c libreswan-3.27/programs/pluto/kernel_mast.c
--- libreswan-3.27-orig/programs/pluto/kernel_mast.c	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/programs/pluto/kernel_mast.c	2019-02-15 16:32:29.001835546 -0500
@@ -48,7 +48,6 @@
 #include "nat_traversal.h"
 #include "server.h"
 
-#include "alg_info.h"
 #include "kernel_alg.h"
 
 static int next_free_mast_device = -1;
diff -Naur libreswan-3.27-orig/programs/pluto/kernel_pfkey.c libreswan-3.27/programs/pluto/kernel_pfkey.c
--- libreswan-3.27-orig/programs/pluto/kernel_pfkey.c	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/programs/pluto/kernel_pfkey.c	2019-02-15 16:32:29.001835546 -0500
@@ -63,7 +63,6 @@
 #include "nat_traversal.h"
 
 #include "lsw_select.h"
-#include "alg_info.h"
 #include "kernel_alg.h"
 #include "ip_address.h"
 
diff -Naur libreswan-3.27-orig/programs/pluto/plutoalg.c libreswan-3.27/programs/pluto/plutoalg.c
--- libreswan-3.27-orig/programs/pluto/plutoalg.c	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/programs/pluto/plutoalg.c	2019-02-15 16:32:29.002835556 -0500
@@ -34,7 +34,7 @@
 #include "connections.h"
 #include "state.h"
 #include "kernel_alg.h"
-#include "alg_info.h"
+#include "proposals.h"
 #include "ike_alg.h"
 #include "ike_alg_integ.h"
 #include "ike_alg_encrypt.h"
@@ -47,15 +47,16 @@
 #include "ikev1.h"	/* for ikev1_quick_dh() */
 
 static bool kernel_alg_db_add(struct db_context *db_ctx,
-			      const struct proposal_info *esp_info,
+			      const struct proposal *proposal,
 			      lset_t policy, bool logit)
 {
 	int ealg_i = SADB_EALG_NONE;
 
+	struct v1_proposal algs = v1_proposal(proposal);
 	if (policy & POLICY_ENCRYPT) {
-		ealg_i = esp_info->encrypt->common.id[IKEv1_ESP_ID];
+		ealg_i = algs.encrypt->common.id[IKEv1_ESP_ID];
 		/* already checked by the parser? */
-		if (!kernel_alg_encrypt_ok(esp_info->encrypt)) {
+		if (!kernel_alg_encrypt_ok(algs.encrypt)) {
 			if (logit) {
 				loglog(RC_LOG_SERIOUS,
 				       "requested kernel enc ealg_id=%d not present",
@@ -68,10 +69,10 @@
 		}
 	}
 
-	int aalg_i = esp_info->integ->integ_ikev1_ah_transform;
+	int aalg_i = algs.integ->integ_ikev1_ah_transform;
 
 	/* already checked by the parser? */
-	if (!kernel_alg_integ_ok(esp_info->integ)) {
+	if (!kernel_alg_integ_ok(algs.integ)) {
 		DBG_log("kernel_alg_db_add() kernel auth aalg_id=%d not present",
 			aalg_i);
 		return FALSE;
@@ -82,32 +83,32 @@
 		db_trans_add(db_ctx, ealg_i);
 
 		/* add ESP auth attr (if present) */
-		if (esp_info->integ != &ike_alg_integ_none) {
+		if (algs.integ != &ike_alg_integ_none) {
 			db_attr_add_values(db_ctx,
 					   AUTH_ALGORITHM,
-					   esp_info->integ->common.id[IKEv1_ESP_ID]);
+					   algs.integ->common.id[IKEv1_ESP_ID]);
 		}
 
 		/* add keylength if specified in esp= string */
-		if (esp_info->enckeylen != 0) {
-				db_attr_add_values(db_ctx,
-						   KEY_LENGTH,
-						   esp_info->enckeylen);
+		if (algs.enckeylen != 0) {
+			db_attr_add_values(db_ctx,
+					   KEY_LENGTH,
+					   algs.enckeylen);
 		} else {
 			/* no key length - if required add default here and add another max entry */
-			int def_ks = (esp_info->encrypt->keylen_omitted ? 0
-				      : esp_info->encrypt->keydeflen);
+			int def_ks = (algs.encrypt->keylen_omitted ? 0
+					: algs.encrypt->keydeflen);
 
 			if (def_ks != 0) {
 				db_attr_add_values(db_ctx, KEY_LENGTH, def_ks);
 				/* add this trans again with max key size */
-				int max_ks = encrypt_max_key_bit_length(esp_info->encrypt);
+				int max_ks = encrypt_max_key_bit_length(algs.encrypt);
 				if (def_ks != max_ks) {
 					db_trans_add(db_ctx, ealg_i);
-					if (esp_info->integ != &ike_alg_integ_none) {
+					if (algs.integ != &ike_alg_integ_none) {
 						db_attr_add_values(db_ctx,
 							AUTH_ALGORITHM,
-							esp_info->integ->common.id[IKEv1_ESP_ID]);
+							algs.integ->common.id[IKEv1_ESP_ID]);
 					}
 					db_attr_add_values(db_ctx,
 						KEY_LENGTH,
@@ -120,8 +121,8 @@
 		db_trans_add(db_ctx, aalg_i);
 
 		/* add ESP auth attr */
-		db_attr_add_values(db_ctx,
-				   AUTH_ALGORITHM, esp_info->integ->common.id[IKEv1_ESP_ID]);
+		db_attr_add_values(db_ctx, AUTH_ALGORITHM,
+				algs.integ->common.id[IKEv1_ESP_ID]);
 	}
 
 	return TRUE;
@@ -135,7 +136,7 @@
  *	for now this function does free() previous returned
  *	malloced pointer (this quirk allows easier spdb.c change)
  */
-static struct db_context *kernel_alg_db_new(struct alg_info_esp *alg_info,
+static struct db_context *kernel_alg_db_new(struct child_proposals proposals,
 					    lset_t policy, bool logit)
 {
 	unsigned int trans_cnt = 0;
@@ -157,25 +158,24 @@
 
 	/*
 	 *      Loop: for each element (struct esp_info) of
-	 *      alg_info, if kernel support is present then
+	 *      proposals, if kernel support is present then
 	 *      build the transform (and attrs)
 	 *
-	 *      if NULL alg_info, propose everything ...
+	 *      if NULL proposals, propose everything ...
 	 */
 
 	bool success = TRUE;
-	if (alg_info != NULL) {
-		FOR_EACH_ESP_INFO(alg_info, proposal) {
+	if (proposals.p != NULL) {
+		FOR_EACH_PROPOSAL(proposals.p, proposal) {
 			LSWDBGP(DBG_CONTROL | DBG_EMITTING, buf) {
 				lswlogs(buf, "adding proposal: ");
-				lswlog_proposal_info(buf, proposal);
+				fmt_proposal(buf, proposal);
 			}
-			if (!kernel_alg_db_add(ctx_new, proposal,
-					       policy, logit))
+			if (!kernel_alg_db_add(ctx_new, proposal, policy, logit))
 				success = FALSE;	/* ??? should we break? */
 		}
 	} else {
-		PEXPECT_LOG("%s", "alg_info should be non-NULL");
+		PEXPECT_LOG("%s", "proposals should be non-NULL");
 	}
 
 	if (!success) {
@@ -268,7 +268,7 @@
 		 * If this is NULL and PFS is required then callers fall back to using
 		 * the parent's DH algorithm.
 		 */
-		const struct oakley_group_desc *dh = ikev1_quick_pfs(c->alg_info_esp);
+		const struct oakley_group_desc *dh = ikev1_quick_pfs(c->child_proposals);
 		if (dh != NULL) {
 			pfsbuf = dh->common.fqn;
 		} else {
@@ -278,7 +278,7 @@
 		pfsbuf = "<N/A>";
 	}
 
-	if (c->alg_info_esp != NULL) {
+	if (c->child_proposals.p != NULL) {
 		LSWLOG_WHACK(RC_COMMENT, buf) {
 			/*
 			 * If DH (PFS) was specified in the esp= or
@@ -299,7 +299,7 @@
 			 */
 			lswlogf(buf, "\"%s\"%s:   %s algorithms: ",
 				c->name, instance, satype);
-			lswlog_alg_info(buf, &c->alg_info_esp->ai);
+			fmt_proposals(buf, c->child_proposals.p);
 		}
 	}
 
@@ -326,10 +326,11 @@
 	}
 }
 
-struct db_sa *kernel_alg_makedb(lset_t policy, struct alg_info_esp *ei,
+struct db_sa *kernel_alg_makedb(lset_t policy,
+				struct child_proposals proposals,
 				bool logit)
 {
-	if (ei == NULL) {
+	if (proposals.p == NULL) {
 		struct db_sa *sadb;
 		lset_t pm = POLICY_ENCRYPT | POLICY_AUTHENTICATE;
 
@@ -344,7 +345,7 @@
 		return sadb;
 	}
 
-	struct db_context *dbnew = kernel_alg_db_new(ei, policy, logit);
+	struct db_context *dbnew = kernel_alg_db_new(proposals, policy, logit);
 
 	if (dbnew == NULL) {
 		libreswan_log("failed to translate esp_info to proposal, returning empty");
diff -Naur libreswan-3.27-orig/programs/pluto/spdb.c libreswan-3.27/programs/pluto/spdb.c
--- libreswan-3.27-orig/programs/pluto/spdb.c	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/programs/pluto/spdb.c	2019-02-15 16:32:29.002835556 -0500
@@ -51,7 +51,6 @@
 
 #include "crypto.h"
 
-#include "alg_info.h"
 #include "kernel_alg.h"
 #include "ike_alg.h"
 #include "db_ops.h"
diff -Naur libreswan-3.27-orig/programs/pluto/spdb.h libreswan-3.27/programs/pluto/spdb.h
--- libreswan-3.27-orig/programs/pluto/spdb.h	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/programs/pluto/spdb.h	2019-02-15 16:32:29.002835556 -0500
@@ -171,12 +171,12 @@
 struct alg_info_ike;
 struct alg_info_esp;
 
-extern struct db_sa *oakley_alg_makedb(struct alg_info_ike *ai,
+extern struct db_sa *oakley_alg_makedb(const struct ike_proposals proposals,
 				       enum ikev1_auth_method auth_method,
 				       bool single_dh);
 
 extern struct db_sa *kernel_alg_makedb(lset_t policy,
-				       struct alg_info_esp *ei,
+				       const struct child_proposals proposals,
 				       bool logit);
 
 #endif /*  _SPDB_H_ */
diff -Naur libreswan-3.27-orig/programs/pluto/spdb_struct.c libreswan-3.27/programs/pluto/spdb_struct.c
--- libreswan-3.27-orig/programs/pluto/spdb_struct.c	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/programs/pluto/spdb_struct.c	2019-02-15 16:32:29.003835565 -0500
@@ -45,7 +45,6 @@
 
 #include "crypto.h"
 
-#include "alg_info.h"
 #include "kernel_alg.h"
 #include "ike_alg.h"
 #include "db_ops.h"
@@ -82,30 +81,31 @@
  * one DH group.
  */
 
-static struct db_sa *oakley_alg_mergedb(struct alg_info_ike *ai,
+static struct db_sa *oakley_alg_mergedb(struct ike_proposals ike_proposals,
 					enum ikev1_auth_method auth_method,
 					bool single_dh);
 
-static struct alg_info_ike *ikev1_default_ike_info(void)
+static struct ike_proposals ikev1_default_ike_info(void)
 {
 	static const struct proposal_policy policy = {
-		.ikev1 = TRUE,
+		.version = IKEv1,
+		.check_pfs_vs_dh = false,
 		.alg_is_ok = ike_alg_is_ike,
 		.warning = libreswan_log,
 	};
 
-	char err[100];
-	struct alg_info_ike *defaults =
-		alg_info_ike_create_from_str(&policy, NULL,
-					     err, sizeof(err));
-	if (defaults == NULL) {
-		PEXPECT_LOG("Invalid IKEv1 default algorithms: %s", err);
+	struct proposal_parser *parser = ike_proposal_parser(&policy);
+	struct ike_proposals defaults = { .p = proposals_from_str(parser, NULL), };
+       if (defaults.p == NULL) {
+		PEXPECT_LOG("Invalid IKEv1 default algorithms: %s",
+			parser->error);
 	}
 
+	free_proposal_parser(&parser);
 	return defaults;
 }
 
-struct db_sa *oakley_alg_makedb(struct alg_info_ike *ai,
+struct db_sa *oakley_alg_makedb(const struct ike_proposals ike_proposals,
 				enum ikev1_auth_method auth_method,
 				bool single_dh)
 {
@@ -114,26 +114,25 @@
 	 * standard defaults.
 	 */
 
-	if (ai == NULL) {
+	if (ike_proposals.p == NULL) {
 		DBG(DBG_CONTROL, DBG_log(
 			    "no specific IKE algorithms specified - using defaults"));
-		struct alg_info_ike *default_info
-			= ikev1_default_ike_info();
+		struct ike_proposals default_info = ikev1_default_ike_info();
 		struct db_sa *new_db = oakley_alg_mergedb(default_info,
 							  auth_method,
 							  single_dh);
-		pfree(default_info);
+		proposals_delref(&default_info.p);
 		return new_db;
 	} else {
-		return oakley_alg_mergedb(ai, auth_method, single_dh);
+		return oakley_alg_mergedb(ike_proposals, auth_method, single_dh);
 	}
 }
 
-struct db_sa *oakley_alg_mergedb(struct alg_info_ike *ai,
+struct db_sa *oakley_alg_mergedb(struct ike_proposals ike_proposals,
 				 enum ikev1_auth_method auth_method,
 				 bool single_dh)
 {
-	passert(ai != NULL);
+	passert(ike_proposals.p != NULL);
 
 	struct db_sa *gsp = NULL;
 
@@ -150,26 +149,28 @@
 	 * when creating each item, we will use the first transform
 	 * from the base item as the template.
 	 */
-	FOR_EACH_IKE_INFO(ai, ike_info) {
+	FOR_EACH_PROPOSAL(ike_proposals.p, proposal) {
 		struct db_sa *emp_sp;
 
-		passert(ike_info->encrypt);
-		passert(ike_info->prf);
-		passert(ike_info->dh);
-
-		unsigned ealg = ike_info->encrypt->common.ikev1_oakley_id;
-		unsigned halg = ike_info->prf->common.ikev1_oakley_id;
-		unsigned modp = ike_info->dh->group;
-		unsigned eklen = ike_info->enckeylen;
+		struct v1_proposal algs = v1_proposal(proposal);
+
+		passert(algs.encrypt != NULL);
+		passert(algs.prf != NULL);
+		passert(algs.dh != NULL);
+
+		unsigned ealg = algs.encrypt->common.ikev1_oakley_id;
+		unsigned halg = algs.prf->common.ikev1_oakley_id;
+		unsigned modp = algs.dh->group;
+		unsigned eklen = algs.enckeylen;
 
 		DBG(DBG_CONTROL,
 		    DBG_log("oakley_alg_makedb() processing ealg=%s=%u halg=%s=%u modp=%s=%u eklen=%u",
-			    ike_info->encrypt->common.name, ealg,
-			    ike_info->prf->common.name, halg,
-			    ike_info->dh->common.name, modp,
+			    algs.encrypt->common.name, ealg,
+			    algs.prf->common.name, halg,
+			    algs.dh->common.name, modp,
 			    eklen));
 
-		const struct encrypt_desc *enc_desc = ike_info->encrypt;
+		const struct encrypt_desc *enc_desc = algs.encrypt;
 		if (eklen != 0 && !encrypt_has_key_bit_length(enc_desc, eklen)) {
 			PEXPECT_LOG("IKEv1 proposal with ENCRYPT%s (specified) keylen:%d, not valid, should have been dropped",
 				    enc_desc->common.name,
@@ -251,7 +252,7 @@
 		 * a different DH group, we try to deal with this.
 		 */
 		if (single_dh && transcnt > 0 &&
-		    ike_info->dh->group != last_modp) {
+		    algs.dh->group != last_modp) {
 			if (
 #ifdef USE_DH2
 			    last_modp == OAKLEY_GROUP_MODP1024 ||
@@ -269,13 +270,13 @@
 				}
 
 				loglog(RC_LOG_SERIOUS,
-				       "transform (%s,%s,%s keylen %zd) ignored.",
+				       "transform (%s,%s,%s keylen %d) ignored.",
 				       enum_name(&oakley_enc_names,
-						 ike_info->encrypt->common.ikev1_oakley_id),
+						 algs.encrypt->common.ikev1_oakley_id),
 				       enum_name(&oakley_hash_names,
-						 ike_info->prf->common.ikev1_oakley_id),
-				       ike_info->dh->common.name,
-				       ike_info->enckeylen);
+				       		algs.prf->common.ikev1_oakley_id),
+					algs.dh->common.name,
+					algs.enckeylen);
 				free_sa(&emp_sp);
 			} else {
 				/*
@@ -303,9 +304,9 @@
 			  * Exclude 3des et.al. which do not include
 			  * default key lengths in the proposal.
 			  */
-			 if (ike_info->enckeylen == 0 &&
-			     !ike_info->encrypt->keylen_omitted) {
-				const struct encrypt_desc *enc_desc = ike_info->encrypt;
+			if (algs.enckeylen == 0 &&
+			    !algs.encrypt->keylen_omitted) {
+				const struct encrypt_desc *enc_desc = algs.encrypt;
 				int def_ks = enc_desc->keydeflen;
 				passert(def_ks); /* ike=null not supported */
 				int max_ks = encrypt_max_key_bit_length(enc_desc);
@@ -375,7 +376,7 @@
 					emp_sp = NULL;
 				}
 			}
-			last_modp = ike_info->dh->group;
+			last_modp = algs.dh->group;
 		}
 
 		transcnt++;
diff -Naur libreswan-3.27-orig/programs/pluto/terminate.c libreswan-3.27/programs/pluto/terminate.c
--- libreswan-3.27-orig/programs/pluto/terminate.c	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/programs/pluto/terminate.c	2019-02-15 16:32:29.003835565 -0500
@@ -53,7 +53,6 @@
 #include "log.h"
 #include "keys.h"
 #include "whack.h"
-#include "alg_info.h"
 #include "spdb.h"
 #include "ike_alg.h"
 #include "kernel_alg.h"
diff -Naur libreswan-3.27-orig/programs/spi/spi.c libreswan-3.27/programs/spi/spi.c
--- libreswan-3.27-orig/programs/spi/spi.c	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/programs/spi/spi.c	2019-02-15 16:32:29.003835565 -0500
@@ -61,7 +61,6 @@
 
 #include "lswlog.h"
 #include "lswtool.h"
-#include "alg_info.h"
 #include "kernel_alg.h"
 #include "kernel_sadb.h"
 #include "pfkey_help.h"
diff -Naur libreswan-3.27-orig/testing/pluto/algparse-01/algparse.v1.pfs.txt libreswan-3.27/testing/pluto/algparse-01/algparse.v1.pfs.txt
--- libreswan-3.27-orig/testing/pluto/algparse-01/algparse.v1.pfs.txt	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/testing/pluto/algparse-01/algparse.v1.pfs.txt	2019-02-15 16:46:01.379390213 -0500
@@ -386,3 +386,9 @@
 	ERROR: IKE PRF algorithm 'id2' is not recognized
 algparse -v1 -pfs 'ike=aes_ccm'
 	ERROR: IKE encryption algorithm 'aes_ccm' is not supported by IKEv1
+algparse -v1 -pfs 'ike=aes_gcm-sha1-none-modp2048'
+	ERROR: IKE encryption algorithm 'aes_gcm' is not supported by IKEv1
+algparse -v1 -pfs 'ike=aes_gcm+aes_gcm-sha1-none-modp2048'
+	ERROR: IKE encryption algorithm 'aes_gcm+aes_gcm' is not recognized
+algparse -v1 -pfs 'ike=aes+aes_gcm'
+	ERROR: IKE encryption algorithm 'aes+aes_gcm' is not recognized
diff -Naur libreswan-3.27-orig/testing/pluto/algparse-01/algparse.v1.txt libreswan-3.27/testing/pluto/algparse-01/algparse.v1.txt
--- libreswan-3.27-orig/testing/pluto/algparse-01/algparse.v1.txt	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/testing/pluto/algparse-01/algparse.v1.txt	2019-02-15 16:46:01.379390213 -0500
@@ -418,3 +418,9 @@
 	ERROR: IKE PRF algorithm 'id2' is not recognized
 algparse -v1 'ike=aes_ccm'
 	ERROR: IKE encryption algorithm 'aes_ccm' is not supported by IKEv1
+algparse -v1 'ike=aes_gcm-sha1-none-modp2048'
+	ERROR: IKE encryption algorithm 'aes_gcm' is not supported by IKEv1
+algparse -v1 'ike=aes_gcm+aes_gcm-sha1-none-modp2048'
+	ERROR: IKE encryption algorithm 'aes_gcm+aes_gcm' is not recognized
+algparse -v1 'ike=aes+aes_gcm'
+	ERROR: IKE encryption algorithm 'aes+aes_gcm' is not recognized
diff -Naur libreswan-3.27-orig/testing/pluto/algparse-01/algparse.v2.pfs.txt libreswan-3.27/testing/pluto/algparse-01/algparse.v2.pfs.txt
--- libreswan-3.27-orig/testing/pluto/algparse-01/algparse.v2.pfs.txt	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/testing/pluto/algparse-01/algparse.v2.pfs.txt	2019-02-15 16:50:12.777728085 -0500
@@ -1,11 +1,11 @@
 algparse -v2 -pfs 'esp'
-	AES_CBC-HMAC_SHA1_96
+	AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -v2 -pfs 'esp='
 	ERROR: String ended with invalid char, just after ""
 algparse -v2 -pfs 'esp=aes'
-	AES_CBC-HMAC_SHA1_96
+	AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -v2 -pfs 'esp=aes;modp2048'
-	AES_CBC-HMAC_SHA1_96-MODP2048
+	AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128-MODP2048
 algparse -v2 -pfs 'esp=aes-sha1'
 	AES_CBC-HMAC_SHA1_96
 algparse -v2 -pfs 'esp=aes-sha1'
@@ -13,7 +13,7 @@
 algparse -v2 -pfs 'esp=aes-sha1-modp2048'
 	AES_CBC-HMAC_SHA1_96-MODP2048
 algparse -v2 -pfs 'esp=aes-128'
-	AES_CBC_128-HMAC_SHA1_96
+	AES_CBC_128-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -v2 -pfs 'esp=aes-128-sha1'
 	AES_CBC_128-HMAC_SHA1_96
 algparse -v2 -pfs 'esp=aes-128-sha1'
@@ -41,7 +41,7 @@
 algparse -v2 -pfs 'esp=null-sha1'
 	NULL-HMAC_SHA1_96
 algparse -v2 -pfs 'esp=aes_cbc'
-	AES_CBC-HMAC_SHA1_96
+	AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -v2 -pfs 'esp=aes-sha'
 	AES_CBC-HMAC_SHA1_96
 algparse -v2 -pfs 'esp=aes-sha1'
@@ -73,13 +73,13 @@
 algparse -v2 -pfs 'esp=aes256-sha2_512'
 	AES_CBC_256-HMAC_SHA2_512_256
 algparse -v2 -pfs 'esp=camellia'
-	CAMELLIA_CBC-HMAC_SHA1_96
+	CAMELLIA_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -v2 -pfs 'esp=camellia128'
-	CAMELLIA_CBC_128-HMAC_SHA1_96
+	CAMELLIA_CBC_128-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -v2 -pfs 'esp=camellia192'
-	CAMELLIA_CBC_192-HMAC_SHA1_96
+	CAMELLIA_CBC_192-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -v2 -pfs 'esp=camellia256'
-	CAMELLIA_CBC_256-HMAC_SHA1_96
+	CAMELLIA_CBC_256-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -v2 -pfs 'esp=aes_ccm'
 	AES_CCM_16-NONE
 algparse -v2 -pfs 'esp=aes_ccm_a-128-null'
@@ -181,19 +181,19 @@
 algparse -v2 -pfs 'esp=aes_gcm_16_256-null'
 	AES_GCM_16_256-NONE
 algparse -v2 -pfs 'esp=aes_ctr'
-	AES_CTR-HMAC_SHA1_96
+	AES_CTR-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -v2 -pfs 'esp=aesctr'
-	AES_CTR-HMAC_SHA1_96
+	AES_CTR-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -v2 -pfs 'esp=aes_ctr128'
-	AES_CTR_128-HMAC_SHA1_96
+	AES_CTR_128-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -v2 -pfs 'esp=aes_ctr192'
-	AES_CTR_192-HMAC_SHA1_96
+	AES_CTR_192-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -v2 -pfs 'esp=aes_ctr256'
-	AES_CTR_256-HMAC_SHA1_96
+	AES_CTR_256-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -v2 -pfs 'esp=serpent'
-	SERPENT_CBC-HMAC_SHA1_96
+	SERPENT_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -v2 -pfs 'esp=twofish'
-	TWOFISH_CBC-HMAC_SHA1_96
+	TWOFISH_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -v2 -pfs 'esp=camellia_cbc_256-hmac_sha2_512_256;modp8192'
 	CAMELLIA_CBC_256-HMAC_SHA2_512_256-MODP8192
 algparse -v2 -pfs 'esp=null_auth_aes_gmac_256-null;modp8192'
@@ -210,12 +210,12 @@
 algparse -v2 -pfs 'esp=aes-sha1-modp8192,aes-sha1-modp8192,aes-sha1-modp8192'
 	AES_CBC-HMAC_SHA1_96-MODP8192
 algparse -v2 -pfs 'esp=aes;none'
-	AES_CBC-HMAC_SHA1_96-NONE
+	AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128-NONE
 algparse -v2 -pfs 'esp=aes;none,aes'
 	ERROR: either all or no ESP proposals should specify DH
 algparse -v2 -pfs 'esp=aes;none,aes;modp2048'
-	AES_CBC-HMAC_SHA1_96-NONE
-	AES_CBC-HMAC_SHA1_96-MODP2048
+	AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128-NONE
+	AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128-MODP2048
 algparse -v2 -pfs 'esp=aes-sha1-none'
 	AES_CBC-HMAC_SHA1_96-NONE
 algparse -v2 -pfs 'esp=aes-sha1;none'
@@ -293,7 +293,7 @@
 algparse -v2 -pfs 'esp=3des-sha1-modp8192,3des-sha2-modp2048'
 	ERROR: more than one IKEv2 ESP DH algorithm (MODP8192, MODP2048) requires unimplemented CHILD_SA INVALID_KE
 algparse -v2 -pfs 'ah'
-	HMAC_SHA1_96
+	HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -v2 -pfs 'ah='
 	ERROR: String ended with invalid char, just after ""
 algparse -v2 -pfs 'ah=md5'
@@ -345,32 +345,23 @@
 algparse -v2 -pfs 'ah=ripemd'
 	ERROR: AH integrity algorithm 'ripemd' is not recognized
 algparse -v2 -pfs 'ike'
-	AES_CBC-HMAC_SHA2_256-MODP2048
-	AES_CBC-HMAC_SHA2_512-MODP2048
-	AES_CBC-HMAC_SHA1-MODP2048
-	3DES_CBC-HMAC_SHA2_256-MODP2048
-	3DES_CBC-HMAC_SHA2_512-MODP2048
-	3DES_CBC-HMAC_SHA1-MODP2048
+	AES_CBC+3DES_CBC-HMAC_SHA2_512+HMAC_SHA2_256-MODP2048+MODP3072+MODP4096+MODP8192+DH19+DH20+DH21+DH31
 algparse -v2 -pfs 'ike='
 	ERROR: String ended with invalid char, just after ""
 algparse -v2 -pfs 'ike=3des-sha1'
-	3DES_CBC-HMAC_SHA1-MODP2048
+	3DES_CBC-HMAC_SHA1-MODP2048+MODP3072+MODP4096+MODP8192+DH19+DH20+DH21+DH31
 algparse -v2 -pfs 'ike=3des-sha1'
-	3DES_CBC-HMAC_SHA1-MODP2048
+	3DES_CBC-HMAC_SHA1-MODP2048+MODP3072+MODP4096+MODP8192+DH19+DH20+DH21+DH31
 algparse -v2 -pfs 'ike=3des-sha1;modp1536'
 	3DES_CBC-HMAC_SHA1-MODP1536
 algparse -v2 -pfs 'ike=3des;dh21'
-	3DES_CBC-HMAC_SHA2_256-DH21
-	3DES_CBC-HMAC_SHA2_512-DH21
-	3DES_CBC-HMAC_SHA1-DH21
+	3DES_CBC-HMAC_SHA2_512+HMAC_SHA2_256-DH21
 algparse -v2 -pfs 'ike=3des-sha1;dh21'
 	3DES_CBC-HMAC_SHA1-DH21
 algparse -v2 -pfs 'ike=3des-sha1-ecp_521'
 	3DES_CBC-HMAC_SHA1-DH21
 algparse -v2 -pfs 'ike=aes_gcm'
-	AES_GCM_16-HMAC_SHA2_256-MODP2048
-	AES_GCM_16-HMAC_SHA2_512-MODP2048
-	AES_GCM_16-HMAC_SHA1-MODP2048
+	AES_GCM_16-HMAC_SHA2_512+HMAC_SHA2_256-MODP2048+MODP3072+MODP4096+MODP8192+DH19+DH20+DH21+DH31
 algparse -v2 -pfs 'ike=aes-sha1-modp8192,aes-sha1-modp8192,aes-sha1-modp8192'
 	AES_CBC-HMAC_SHA1-MODP8192
 algparse -v2 -pfs 'ike=aes;none'
@@ -381,3 +372,10 @@
 	ERROR: IKE PRF algorithm 'id2' is not recognized
 algparse -v2 -pfs 'ike=aes_ccm'
 	ERROR: IKE encryption algorithm 'aes_ccm' is not supported
+algparse -v2 -pfs 'ike=aes_gcm-sha1-none-modp2048'
+	ERROR: 'modp2048' unexpected
+algparse -v2 -pfs 'ike=aes_gcm+aes_gcm-sha1-none-modp2048'
+	WARNING: discarding duplicate algorithm 'aes_gcm_16'
+	ERROR: 'modp2048' unexpected
+algparse -v2 -pfs 'ike=aes+aes_gcm'
+	ERROR: AEAD and non-AEAD IKE encryption algorithm can not be combined
diff -Naur libreswan-3.27-orig/testing/pluto/algparse-01/algparse.v2.txt libreswan-3.27/testing/pluto/algparse-01/algparse.v2.txt
--- libreswan-3.27-orig/testing/pluto/algparse-01/algparse.v2.txt	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/testing/pluto/algparse-01/algparse.v2.txt	2019-02-15 16:50:12.778728094 -0500
@@ -1,12 +1,12 @@
 algparse -v2 'esp'
-	AES_CBC-HMAC_SHA1_96
+	AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -v2 'esp='
 	ERROR: String ended with invalid char, just after ""
 algparse -v2 'esp=aes'
-	AES_CBC-HMAC_SHA1_96
+	AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -v2 'esp=aes;modp2048'
 	WARNING: ignoring ESP DH algorithm MODP2048 as PFS policy is disabled
-	AES_CBC-HMAC_SHA1_96
+	AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -v2 'esp=aes-sha1'
 	AES_CBC-HMAC_SHA1_96
 algparse -v2 'esp=aes-sha1'
@@ -15,7 +15,7 @@
 	WARNING: ignoring ESP DH algorithm MODP2048 as PFS policy is disabled
 	AES_CBC-HMAC_SHA1_96
 algparse -v2 'esp=aes-128'
-	AES_CBC_128-HMAC_SHA1_96
+	AES_CBC_128-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -v2 'esp=aes-128-sha1'
 	AES_CBC_128-HMAC_SHA1_96
 algparse -v2 'esp=aes-128-sha1'
@@ -49,7 +49,7 @@
 algparse -v2 'esp=null-sha1'
 	NULL-HMAC_SHA1_96
 algparse -v2 'esp=aes_cbc'
-	AES_CBC-HMAC_SHA1_96
+	AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -v2 'esp=aes-sha'
 	AES_CBC-HMAC_SHA1_96
 algparse -v2 'esp=aes-sha1'
@@ -81,13 +81,13 @@
 algparse -v2 'esp=aes256-sha2_512'
 	AES_CBC_256-HMAC_SHA2_512_256
 algparse -v2 'esp=camellia'
-	CAMELLIA_CBC-HMAC_SHA1_96
+	CAMELLIA_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -v2 'esp=camellia128'
-	CAMELLIA_CBC_128-HMAC_SHA1_96
+	CAMELLIA_CBC_128-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -v2 'esp=camellia192'
-	CAMELLIA_CBC_192-HMAC_SHA1_96
+	CAMELLIA_CBC_192-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -v2 'esp=camellia256'
-	CAMELLIA_CBC_256-HMAC_SHA1_96
+	CAMELLIA_CBC_256-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -v2 'esp=aes_ccm'
 	AES_CCM_16-NONE
 algparse -v2 'esp=aes_ccm_a-128-null'
@@ -189,19 +189,19 @@
 algparse -v2 'esp=aes_gcm_16_256-null'
 	AES_GCM_16_256-NONE
 algparse -v2 'esp=aes_ctr'
-	AES_CTR-HMAC_SHA1_96
+	AES_CTR-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -v2 'esp=aesctr'
-	AES_CTR-HMAC_SHA1_96
+	AES_CTR-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -v2 'esp=aes_ctr128'
-	AES_CTR_128-HMAC_SHA1_96
+	AES_CTR_128-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -v2 'esp=aes_ctr192'
-	AES_CTR_192-HMAC_SHA1_96
+	AES_CTR_192-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -v2 'esp=aes_ctr256'
-	AES_CTR_256-HMAC_SHA1_96
+	AES_CTR_256-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -v2 'esp=serpent'
-	SERPENT_CBC-HMAC_SHA1_96
+	SERPENT_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -v2 'esp=twofish'
-	TWOFISH_CBC-HMAC_SHA1_96
+	TWOFISH_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -v2 'esp=camellia_cbc_256-hmac_sha2_512_256;modp8192'
 	WARNING: ignoring ESP DH algorithm MODP8192 as PFS policy is disabled
 	CAMELLIA_CBC_256-HMAC_SHA2_512_256
@@ -228,16 +228,16 @@
 	AES_CBC-HMAC_SHA1_96
 algparse -v2 'esp=aes;none'
 	WARNING: ignoring redundant ESP DH algorithm NONE as PFS policy is disabled
-	AES_CBC-HMAC_SHA1_96
+	AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -v2 'esp=aes;none,aes'
 	WARNING: ignoring redundant ESP DH algorithm NONE as PFS policy is disabled
-	AES_CBC-HMAC_SHA1_96
-	AES_CBC-HMAC_SHA1_96
+	AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+	AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -v2 'esp=aes;none,aes;modp2048'
 	WARNING: ignoring redundant ESP DH algorithm NONE as PFS policy is disabled
 	WARNING: ignoring ESP DH algorithm MODP2048 as PFS policy is disabled
-	AES_CBC-HMAC_SHA1_96
-	AES_CBC-HMAC_SHA1_96
+	AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+	AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -v2 'esp=aes-sha1-none'
 	WARNING: ignoring redundant ESP DH algorithm NONE as PFS policy is disabled
 	AES_CBC-HMAC_SHA1_96
@@ -332,7 +332,7 @@
 	3DES_CBC-HMAC_SHA1_96
 	3DES_CBC-HMAC_SHA2_256_128
 algparse -v2 'ah'
-	HMAC_SHA1_96
+	HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -v2 'ah='
 	ERROR: String ended with invalid char, just after ""
 algparse -v2 'ah=md5'
@@ -388,32 +388,23 @@
 algparse -v2 'ah=ripemd'
 	ERROR: AH integrity algorithm 'ripemd' is not recognized
 algparse -v2 'ike'
-	AES_CBC-HMAC_SHA2_256-MODP2048
-	AES_CBC-HMAC_SHA2_512-MODP2048
-	AES_CBC-HMAC_SHA1-MODP2048
-	3DES_CBC-HMAC_SHA2_256-MODP2048
-	3DES_CBC-HMAC_SHA2_512-MODP2048
-	3DES_CBC-HMAC_SHA1-MODP2048
+	AES_CBC+3DES_CBC-HMAC_SHA2_512+HMAC_SHA2_256-MODP2048+MODP3072+MODP4096+MODP8192+DH19+DH20+DH21+DH31
 algparse -v2 'ike='
 	ERROR: String ended with invalid char, just after ""
 algparse -v2 'ike=3des-sha1'
-	3DES_CBC-HMAC_SHA1-MODP2048
+	3DES_CBC-HMAC_SHA1-MODP2048+MODP3072+MODP4096+MODP8192+DH19+DH20+DH21+DH31
 algparse -v2 'ike=3des-sha1'
-	3DES_CBC-HMAC_SHA1-MODP2048
+	3DES_CBC-HMAC_SHA1-MODP2048+MODP3072+MODP4096+MODP8192+DH19+DH20+DH21+DH31
 algparse -v2 'ike=3des-sha1;modp1536'
 	3DES_CBC-HMAC_SHA1-MODP1536
 algparse -v2 'ike=3des;dh21'
-	3DES_CBC-HMAC_SHA2_256-DH21
-	3DES_CBC-HMAC_SHA2_512-DH21
-	3DES_CBC-HMAC_SHA1-DH21
+	3DES_CBC-HMAC_SHA2_512+HMAC_SHA2_256-DH21
 algparse -v2 'ike=3des-sha1;dh21'
 	3DES_CBC-HMAC_SHA1-DH21
 algparse -v2 'ike=3des-sha1-ecp_521'
 	3DES_CBC-HMAC_SHA1-DH21
 algparse -v2 'ike=aes_gcm'
-	AES_GCM_16-HMAC_SHA2_256-MODP2048
-	AES_GCM_16-HMAC_SHA2_512-MODP2048
-	AES_GCM_16-HMAC_SHA1-MODP2048
+	AES_GCM_16-HMAC_SHA2_512+HMAC_SHA2_256-MODP2048+MODP3072+MODP4096+MODP8192+DH19+DH20+DH21+DH31
 algparse -v2 'ike=aes-sha1-modp8192,aes-sha1-modp8192,aes-sha1-modp8192'
 	AES_CBC-HMAC_SHA1-MODP8192
 algparse -v2 'ike=aes;none'
@@ -424,3 +415,10 @@
 	ERROR: IKE PRF algorithm 'id2' is not recognized
 algparse -v2 'ike=aes_ccm'
 	ERROR: IKE encryption algorithm 'aes_ccm' is not supported
+algparse -v2 'ike=aes_gcm-sha1-none-modp2048'
+	ERROR: 'modp2048' unexpected
+algparse -v2 'ike=aes_gcm+aes_gcm-sha1-none-modp2048'
+	WARNING: discarding duplicate algorithm 'aes_gcm_16'
+	ERROR: 'modp2048' unexpected
+algparse -v2 'ike=aes+aes_gcm'
+	ERROR: AEAD and non-AEAD IKE encryption algorithm can not be combined
diff -Naur libreswan-3.27-orig/testing/pluto/algparse-01/algparse.v.txt libreswan-3.27/testing/pluto/algparse-01/algparse.v.txt
--- libreswan-3.27-orig/testing/pluto/algparse-01/algparse.v.txt	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/testing/pluto/algparse-01/algparse.v.txt	2019-02-15 16:32:29.004835574 -0500
@@ -13,7 +13,7 @@
 algparse:   SERPENT_CBC             IKEv1: IKE ESP     IKEv2: IKE ESP           {256,192,*128}  serpent
 algparse:   TWOFISH_CBC             IKEv1: IKE ESP     IKEv2: IKE ESP           {256,192,*128}  twofish
 algparse:   TWOFISH_SSH             IKEv1: IKE         IKEv2: IKE ESP           {256,192,*128}  twofish_cbc_ssh
-algparse:   NULL_AUTH_AES_GMAC      IKEv1:     ESP     IKEv2:     ESP           {256,192,*128}  aes_gmac
+algparse:   NULL_AUTH_AES_GMAC      IKEv1:     ESP     IKEv2:     ESP     FIPS  {256,192,*128}  aes_gmac
 algparse:   NULL                    IKEv1:     ESP     IKEv2:     ESP           []
 algparse:   CHACHA20_POLY1305       IKEv1:             IKEv2: IKE ESP           [*256]  chacha20poly1305
 algparse: Hash algorithms:
@@ -28,17 +28,17 @@
 algparse:   HMAC_SHA2_256           IKEv1: IKE         IKEv2: IKE         FIPS  sha2, sha256, sha2_256
 algparse:   HMAC_SHA2_384           IKEv1: IKE         IKEv2: IKE         FIPS  sha384, sha2_384
 algparse:   HMAC_SHA2_512           IKEv1: IKE         IKEv2: IKE         FIPS  sha512, sha2_512
-algparse:   AES_XCBC                IKEv1:             IKEv2: IKE         FIPS  aes128_xcbc
+algparse:   AES_XCBC                IKEv1:             IKEv2: IKE               aes128_xcbc
 algparse: Integrity algorithms:
 algparse:   HMAC_MD5_96             IKEv1: IKE ESP AH  IKEv2: IKE ESP AH        md5, hmac_md5
 algparse:   HMAC_SHA1_96            IKEv1: IKE ESP AH  IKEv2: IKE ESP AH  FIPS  sha, sha1, sha1_96, hmac_sha1
-algparse:   HMAC_SHA2_512_256       IKEv1: IKE ESP AH  IKEv2: IKE ESP AH  FIPS  sha512, sha2_512, hmac_sha2_512
-algparse:   HMAC_SHA2_384_192       IKEv1: IKE ESP AH  IKEv2: IKE ESP AH  FIPS  sha384, sha2_384, hmac_sha2_384
-algparse:   HMAC_SHA2_256_128       IKEv1: IKE ESP AH  IKEv2: IKE ESP AH  FIPS  sha2, sha256, sha2_256, hmac_sha2_256
+algparse:   HMAC_SHA2_512_256       IKEv1: IKE ESP AH  IKEv2: IKE ESP AH  FIPS  sha512, sha2_512, sha2_512_256, hmac_sha2_512
+algparse:   HMAC_SHA2_384_192       IKEv1: IKE ESP AH  IKEv2: IKE ESP AH  FIPS  sha384, sha2_384, sha2_384_192, hmac_sha2_384
+algparse:   HMAC_SHA2_256_128       IKEv1: IKE ESP AH  IKEv2: IKE ESP AH  FIPS  sha2, sha256, sha2_256, sha2_256_128, hmac_sha2_256
 algparse:   HMAC_SHA2_256_TRUNCBUG  IKEv1:     ESP AH  IKEv2:         AH      
-algparse:   AES_XCBC_96             IKEv1:     ESP AH  IKEv2: IKE ESP AH  FIPS  aes_xcbc, aes128_xcbc, aes128_xcbc_96
+algparse:   AES_XCBC_96             IKEv1:     ESP AH  IKEv2: IKE ESP AH        aes_xcbc, aes128_xcbc, aes128_xcbc_96
 algparse:   AES_CMAC_96             IKEv1:     ESP AH  IKEv2:     ESP AH  FIPS  aes_cmac
-algparse:   NONE                    IKEv1:     ESP     IKEv2:     ESP     FIPS  null
+algparse:   NONE                    IKEv1:     ESP     IKEv2: IKE ESP     FIPS  null
 algparse: DH algorithms:
 algparse:   NONE                    IKEv1:             IKEv2: IKE ESP AH  FIPS  null, dh0
 algparse:   MODP1024                IKEv1: IKE ESP AH  IKEv2: IKE ESP AH        dh2
@@ -48,8 +48,8 @@
 algparse:   MODP4096                IKEv1: IKE ESP AH  IKEv2: IKE ESP AH  FIPS  dh16
 algparse:   MODP6144                IKEv1: IKE ESP AH  IKEv2: IKE ESP AH  FIPS  dh17
 algparse:   MODP8192                IKEv1: IKE ESP AH  IKEv2: IKE ESP AH  FIPS  dh18
-algparse:   DH19                    IKEv1: IKE         IKEv2: IKE ESP AH  FIPS  ecp_256
-algparse:   DH20                    IKEv1: IKE         IKEv2: IKE ESP AH  FIPS  ecp_384
-algparse:   DH21                    IKEv1: IKE         IKEv2: IKE ESP AH  FIPS  ecp_521
+algparse:   DH19                    IKEv1: IKE         IKEv2: IKE ESP AH  FIPS  ecp_256, ecp256
+algparse:   DH20                    IKEv1: IKE         IKEv2: IKE ESP AH  FIPS  ecp_384, ecp384
+algparse:   DH21                    IKEv1: IKE         IKEv2: IKE ESP AH  FIPS  ecp_521, ecp521
 algparse:   DH31                    IKEv1: IKE         IKEv2: IKE ESP AH        curve25519
 algparse: leak detective found no leaks
diff -Naur libreswan-3.27-orig/testing/pluto/algparse-01/west.conf libreswan-3.27/testing/pluto/algparse-01/west.conf
--- libreswan-3.27-orig/testing/pluto/algparse-01/west.conf	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/testing/pluto/algparse-01/west.conf	2019-02-15 16:32:29.004835574 -0500
@@ -10,4 +10,7 @@
 	protostack=netkey
 	plutodebug=all
 
+conn %default
+	ikev2=no
+
 include	/testing/baseconfigs/all/etc/ipsec.d/ipsec.conf.common
diff -Naur libreswan-3.27-orig/testing/pluto/algparse-01/west.console.txt libreswan-3.27/testing/pluto/algparse-01/west.console.txt
--- libreswan-3.27-orig/testing/pluto/algparse-01/west.console.txt	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/testing/pluto/algparse-01/west.console.txt	2019-02-15 16:32:29.005835584 -0500
@@ -5,8 +5,6 @@
  ../bin/algparse.sh PATH/libexec/ipsec/algparse algparse*.txt
 PATH/libexec/ipsec/algparse -v1 -pfs -t # algparse.v1.pfs.txt
 PATH/libexec/ipsec/algparse -v1 -t # algparse.v1.txt
-PATH/libexec/ipsec/algparse -v1 -v2 -pfs -t # algparse.v1.v2.pfs.txt
-PATH/libexec/ipsec/algparse -v1 -v2 -t # algparse.v1.v2.txt
 PATH/libexec/ipsec/algparse -v2 -pfs -t # algparse.v2.pfs.txt
 PATH/libexec/ipsec/algparse -v2 -t # algparse.v2.txt
 PATH/libexec/ipsec/algparse -v # algparse.v.txt
@@ -48,7 +46,7 @@
   SERPENT_CBC             IKEv1: IKE ESP     IKEv2: IKE ESP           {256,192,*128}  serpent
   TWOFISH_CBC             IKEv1: IKE ESP     IKEv2: IKE ESP           {256,192,*128}  twofish
   TWOFISH_SSH             IKEv1: IKE         IKEv2: IKE ESP           {256,192,*128}  twofish_cbc_ssh
-  NULL_AUTH_AES_GMAC      IKEv1:     ESP     IKEv2:     ESP           {256,192,*128}  aes_gmac
+  NULL_AUTH_AES_GMAC      IKEv1:     ESP     IKEv2:     ESP     FIPS  {256,192,*128}  aes_gmac
   NULL                    IKEv1:     ESP     IKEv2:     ESP           []
   CHACHA20_POLY1305       IKEv1:             IKEv2: IKE ESP           [*256]  chacha20poly1305
 Hash algorithms:
@@ -63,7 +61,7 @@
   HMAC_SHA2_256           IKEv1: IKE         IKEv2: IKE         FIPS  sha2, sha256, sha2_256
   HMAC_SHA2_384           IKEv1: IKE         IKEv2: IKE         FIPS  sha384, sha2_384
   HMAC_SHA2_512           IKEv1: IKE         IKEv2: IKE         FIPS  sha512, sha2_512
-  AES_XCBC                IKEv1:             IKEv2: IKE         FIPS  aes128_xcbc
+  AES_XCBC                IKEv1:             IKEv2: IKE               aes128_xcbc
 Integrity algorithms:
   HMAC_MD5_96             IKEv1: IKE ESP AH  IKEv2: IKE ESP AH        md5, hmac_md5
   HMAC_SHA1_96            IKEv1: IKE ESP AH  IKEv2: IKE ESP AH  FIPS  sha, sha1, sha1_96, hmac_sha1
@@ -71,7 +69,7 @@
   HMAC_SHA2_384_192       IKEv1: IKE ESP AH  IKEv2: IKE ESP AH  FIPS  sha384, sha2_384, hmac_sha2_384
   HMAC_SHA2_256_128       IKEv1: IKE ESP AH  IKEv2: IKE ESP AH  FIPS  sha2, sha256, sha2_256, hmac_sha2_256
   HMAC_SHA2_256_TRUNCBUG  IKEv1:     ESP AH  IKEv2:         AH      
-  AES_XCBC_96             IKEv1:     ESP AH  IKEv2: IKE ESP AH  FIPS  aes_xcbc, aes128_xcbc, aes128_xcbc_96
+  AES_XCBC_96             IKEv1:     ESP AH  IKEv2: IKE ESP AH        aes_xcbc, aes128_xcbc, aes128_xcbc_96
   AES_CMAC_96             IKEv1:     ESP AH  IKEv2:     ESP AH  FIPS  aes_cmac
   NONE                    IKEv1:     ESP     IKEv2:     ESP     FIPS  null
 DH algorithms:
diff -Naur libreswan-3.27-orig/testing/pluto/algparse-02-fips/algparse.v1.pfs.txt libreswan-3.27/testing/pluto/algparse-02-fips/algparse.v1.pfs.txt
--- libreswan-3.27-orig/testing/pluto/algparse-02-fips/algparse.v1.pfs.txt	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/testing/pluto/algparse-02-fips/algparse.v1.pfs.txt	2019-02-15 16:46:01.380390223 -0500
@@ -57,7 +57,7 @@
 algparse -fips -v1 -pfs 'esp=aes128-sha1'
 	AES_CBC_128-HMAC_SHA1_96
 algparse -fips -v1 -pfs 'esp=aes128-aes_xcbc'
-	AES_CBC_128-AES_XCBC_96
+	ERROR: ESP integrity algorithm 'aes_xcbc' is not supported
 algparse -fips -v1 -pfs 'esp=aes192-sha1'
 	AES_CBC_192-HMAC_SHA1_96
 algparse -fips -v1 -pfs 'esp=aes256-sha1'
@@ -197,7 +197,7 @@
 algparse -fips -v1 -pfs 'esp=camellia_cbc_256-hmac_sha2_512_256;modp8192'
 	ERROR: ESP encryption algorithm 'camellia_cbc_256' is not supported
 algparse -fips -v1 -pfs 'esp=null_auth_aes_gmac_256-null;modp8192'
-	ERROR: ESP encryption algorithm 'null_auth_aes_gmac_256' is not supported
+	NULL_AUTH_AES_GMAC_256-NONE-MODP8192
 algparse -fips -v1 -pfs 'esp=3des-sha1;modp8192'
 	3DES_CBC-HMAC_SHA1_96-MODP8192
 algparse -fips -v1 -pfs 'esp=3des-sha1-modp8192'
@@ -318,7 +318,7 @@
 algparse -fips -v1 -pfs 'ah=sha2_512'
 	HMAC_SHA2_512_256
 algparse -fips -v1 -pfs 'ah=aes_xcbc'
-	AES_XCBC_96
+	ERROR: AH integrity algorithm 'aes_xcbc' is not supported
 algparse -fips -v1 -pfs 'ah=sha2-none'
 	ERROR: AH DH algorithm 'none' is not supported by IKEv1
 algparse -fips -v1 -pfs 'ah=sha2;none'
@@ -378,3 +378,9 @@
 	ERROR: IKE PRF algorithm 'id2' is not recognized
 algparse -fips -v1 -pfs 'ike=aes_ccm'
 	ERROR: IKE encryption algorithm 'aes_ccm' is not supported by IKEv1
+algparse -fips -v1 -pfs 'ike=aes_gcm-sha1-none-modp2048'
+	ERROR: IKE encryption algorithm 'aes_gcm' is not supported by IKEv1
+algparse -fips -v1 -pfs 'ike=aes_gcm+aes_gcm-sha1-none-modp2048'
+	ERROR: IKE encryption algorithm 'aes_gcm+aes_gcm' is not recognized
+algparse -fips -v1 -pfs 'ike=aes+aes_gcm'
+	ERROR: IKE encryption algorithm 'aes+aes_gcm' is not recognized
diff -Naur libreswan-3.27-orig/testing/pluto/algparse-02-fips/algparse.v1.txt libreswan-3.27/testing/pluto/algparse-02-fips/algparse.v1.txt
--- libreswan-3.27-orig/testing/pluto/algparse-02-fips/algparse.v1.txt	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/testing/pluto/algparse-02-fips/algparse.v1.txt	2019-02-15 16:46:01.380390223 -0500
@@ -61,7 +61,7 @@
 algparse -fips -v1 'esp=aes128-sha1'
 	AES_CBC_128-HMAC_SHA1_96
 algparse -fips -v1 'esp=aes128-aes_xcbc'
-	AES_CBC_128-AES_XCBC_96
+	ERROR: ESP integrity algorithm 'aes_xcbc' is not supported
 algparse -fips -v1 'esp=aes192-sha1'
 	AES_CBC_192-HMAC_SHA1_96
 algparse -fips -v1 'esp=aes256-sha1'
@@ -201,7 +201,8 @@
 algparse -fips -v1 'esp=camellia_cbc_256-hmac_sha2_512_256;modp8192'
 	ERROR: ESP encryption algorithm 'camellia_cbc_256' is not supported
 algparse -fips -v1 'esp=null_auth_aes_gmac_256-null;modp8192'
-	ERROR: ESP encryption algorithm 'null_auth_aes_gmac_256' is not supported
+	WARNING: ignoring ESP DH algorithm MODP8192 as PFS policy is disabled
+	NULL_AUTH_AES_GMAC_256-NONE
 algparse -fips -v1 'esp=3des-sha1;modp8192'
 	WARNING: ignoring ESP DH algorithm MODP8192 as PFS policy is disabled
 	3DES_CBC-HMAC_SHA1_96
@@ -345,7 +346,7 @@
 algparse -fips -v1 'ah=sha2_512'
 	HMAC_SHA2_512_256
 algparse -fips -v1 'ah=aes_xcbc'
-	AES_XCBC_96
+	ERROR: AH integrity algorithm 'aes_xcbc' is not supported
 algparse -fips -v1 'ah=sha2-none'
 	ERROR: AH DH algorithm 'none' is not supported by IKEv1
 algparse -fips -v1 'ah=sha2;none'
@@ -406,3 +407,9 @@
 	ERROR: IKE PRF algorithm 'id2' is not recognized
 algparse -fips -v1 'ike=aes_ccm'
 	ERROR: IKE encryption algorithm 'aes_ccm' is not supported by IKEv1
+algparse -fips -v1 'ike=aes_gcm-sha1-none-modp2048'
+	ERROR: IKE encryption algorithm 'aes_gcm' is not supported by IKEv1
+algparse -fips -v1 'ike=aes_gcm+aes_gcm-sha1-none-modp2048'
+	ERROR: IKE encryption algorithm 'aes_gcm+aes_gcm' is not recognized
+algparse -fips -v1 'ike=aes+aes_gcm'
+	ERROR: IKE encryption algorithm 'aes+aes_gcm' is not recognized
diff -Naur libreswan-3.27-orig/testing/pluto/algparse-02-fips/algparse.v2.pfs.txt libreswan-3.27/testing/pluto/algparse-02-fips/algparse.v2.pfs.txt
--- libreswan-3.27-orig/testing/pluto/algparse-02-fips/algparse.v2.pfs.txt	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/testing/pluto/algparse-02-fips/algparse.v2.pfs.txt	2019-02-15 16:50:12.778728094 -0500
@@ -1,11 +1,11 @@
 algparse -fips -v2 -pfs 'esp'
-	AES_CBC-HMAC_SHA1_96
+	AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -fips -v2 -pfs 'esp='
 	ERROR: String ended with invalid char, just after ""
 algparse -fips -v2 -pfs 'esp=aes'
-	AES_CBC-HMAC_SHA1_96
+	AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -fips -v2 -pfs 'esp=aes;modp2048'
-	AES_CBC-HMAC_SHA1_96-MODP2048
+	AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128-MODP2048
 algparse -fips -v2 -pfs 'esp=aes-sha1'
 	AES_CBC-HMAC_SHA1_96
 algparse -fips -v2 -pfs 'esp=aes-sha1'
@@ -13,7 +13,7 @@
 algparse -fips -v2 -pfs 'esp=aes-sha1-modp2048'
 	AES_CBC-HMAC_SHA1_96-MODP2048
 algparse -fips -v2 -pfs 'esp=aes-128'
-	AES_CBC_128-HMAC_SHA1_96
+	AES_CBC_128-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -fips -v2 -pfs 'esp=aes-128-sha1'
 	AES_CBC_128-HMAC_SHA1_96
 algparse -fips -v2 -pfs 'esp=aes-128-sha1'
@@ -41,7 +41,7 @@
 algparse -fips -v2 -pfs 'esp=null-sha1'
 	ERROR: ESP encryption algorithm 'null' is not supported
 algparse -fips -v2 -pfs 'esp=aes_cbc'
-	AES_CBC-HMAC_SHA1_96
+	AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -fips -v2 -pfs 'esp=aes-sha'
 	AES_CBC-HMAC_SHA1_96
 algparse -fips -v2 -pfs 'esp=aes-sha1'
@@ -57,7 +57,7 @@
 algparse -fips -v2 -pfs 'esp=aes128-sha1'
 	AES_CBC_128-HMAC_SHA1_96
 algparse -fips -v2 -pfs 'esp=aes128-aes_xcbc'
-	AES_CBC_128-AES_XCBC_96
+	ERROR: ESP integrity algorithm 'aes_xcbc' is not supported
 algparse -fips -v2 -pfs 'esp=aes192-sha1'
 	AES_CBC_192-HMAC_SHA1_96
 algparse -fips -v2 -pfs 'esp=aes256-sha1'
@@ -181,15 +181,15 @@
 algparse -fips -v2 -pfs 'esp=aes_gcm_16_256-null'
 	AES_GCM_16_256-NONE
 algparse -fips -v2 -pfs 'esp=aes_ctr'
-	AES_CTR-HMAC_SHA1_96
+	AES_CTR-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -fips -v2 -pfs 'esp=aesctr'
-	AES_CTR-HMAC_SHA1_96
+	AES_CTR-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -fips -v2 -pfs 'esp=aes_ctr128'
-	AES_CTR_128-HMAC_SHA1_96
+	AES_CTR_128-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -fips -v2 -pfs 'esp=aes_ctr192'
-	AES_CTR_192-HMAC_SHA1_96
+	AES_CTR_192-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -fips -v2 -pfs 'esp=aes_ctr256'
-	AES_CTR_256-HMAC_SHA1_96
+	AES_CTR_256-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -fips -v2 -pfs 'esp=serpent'
 	ERROR: ESP encryption algorithm 'serpent' is not supported
 algparse -fips -v2 -pfs 'esp=twofish'
@@ -197,7 +197,7 @@
 algparse -fips -v2 -pfs 'esp=camellia_cbc_256-hmac_sha2_512_256;modp8192'
 	ERROR: ESP encryption algorithm 'camellia_cbc_256' is not supported
 algparse -fips -v2 -pfs 'esp=null_auth_aes_gmac_256-null;modp8192'
-	ERROR: ESP encryption algorithm 'null_auth_aes_gmac_256' is not supported
+	NULL_AUTH_AES_GMAC_256-NONE-MODP8192
 algparse -fips -v2 -pfs 'esp=3des-sha1;modp8192'
 	3DES_CBC-HMAC_SHA1_96-MODP8192
 algparse -fips -v2 -pfs 'esp=3des-sha1-modp8192'
@@ -210,12 +210,12 @@
 algparse -fips -v2 -pfs 'esp=aes-sha1-modp8192,aes-sha1-modp8192,aes-sha1-modp8192'
 	AES_CBC-HMAC_SHA1_96-MODP8192
 algparse -fips -v2 -pfs 'esp=aes;none'
-	AES_CBC-HMAC_SHA1_96-NONE
+	AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128-NONE
 algparse -fips -v2 -pfs 'esp=aes;none,aes'
 	ERROR: either all or no ESP proposals should specify DH
 algparse -fips -v2 -pfs 'esp=aes;none,aes;modp2048'
-	AES_CBC-HMAC_SHA1_96-NONE
-	AES_CBC-HMAC_SHA1_96-MODP2048
+	AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128-NONE
+	AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128-MODP2048
 algparse -fips -v2 -pfs 'esp=aes-sha1-none'
 	AES_CBC-HMAC_SHA1_96-NONE
 algparse -fips -v2 -pfs 'esp=aes-sha1;none'
@@ -293,7 +293,7 @@
 algparse -fips -v2 -pfs 'esp=3des-sha1-modp8192,3des-sha2-modp2048'
 	ERROR: more than one IKEv2 ESP DH algorithm (MODP8192, MODP2048) requires unimplemented CHILD_SA INVALID_KE
 algparse -fips -v2 -pfs 'ah'
-	HMAC_SHA1_96
+	HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -fips -v2 -pfs 'ah='
 	ERROR: String ended with invalid char, just after ""
 algparse -fips -v2 -pfs 'ah=md5'
@@ -319,7 +319,7 @@
 algparse -fips -v2 -pfs 'ah=sha2_512'
 	HMAC_SHA2_512_256
 algparse -fips -v2 -pfs 'ah=aes_xcbc'
-	AES_XCBC_96
+	ERROR: AH integrity algorithm 'aes_xcbc' is not supported
 algparse -fips -v2 -pfs 'ah=sha2-none'
 	HMAC_SHA2_256_128-NONE
 algparse -fips -v2 -pfs 'ah=sha2;none'
@@ -345,32 +345,23 @@
 algparse -fips -v2 -pfs 'ah=ripemd'
 	ERROR: AH integrity algorithm 'ripemd' is not recognized
 algparse -fips -v2 -pfs 'ike'
-	AES_CBC-HMAC_SHA2_256-MODP2048
-	AES_CBC-HMAC_SHA2_512-MODP2048
-	AES_CBC-HMAC_SHA1-MODP2048
-	3DES_CBC-HMAC_SHA2_256-MODP2048
-	3DES_CBC-HMAC_SHA2_512-MODP2048
-	3DES_CBC-HMAC_SHA1-MODP2048
+	AES_CBC+3DES_CBC-HMAC_SHA2_512+HMAC_SHA2_256-MODP2048+MODP3072+MODP4096+MODP8192+DH19+DH20+DH21+DH31
 algparse -fips -v2 -pfs 'ike='
 	ERROR: String ended with invalid char, just after ""
 algparse -fips -v2 -pfs 'ike=3des-sha1'
-	3DES_CBC-HMAC_SHA1-MODP2048
+	3DES_CBC-HMAC_SHA1-MODP2048+MODP3072+MODP4096+MODP8192+DH19+DH20+DH21+DH31
 algparse -fips -v2 -pfs 'ike=3des-sha1'
-	3DES_CBC-HMAC_SHA1-MODP2048
+	3DES_CBC-HMAC_SHA1-MODP2048+MODP3072+MODP4096+MODP8192+DH19+DH20+DH21+DH31
 algparse -fips -v2 -pfs 'ike=3des-sha1;modp1536'
 	ERROR: IKE DH algorithm 'modp1536' is not supported
 algparse -fips -v2 -pfs 'ike=3des;dh21'
-	3DES_CBC-HMAC_SHA2_256-DH21
-	3DES_CBC-HMAC_SHA2_512-DH21
-	3DES_CBC-HMAC_SHA1-DH21
+	3DES_CBC-HMAC_SHA2_512+HMAC_SHA2_256-DH21
 algparse -fips -v2 -pfs 'ike=3des-sha1;dh21'
 	3DES_CBC-HMAC_SHA1-DH21
 algparse -fips -v2 -pfs 'ike=3des-sha1-ecp_521'
 	3DES_CBC-HMAC_SHA1-DH21
 algparse -fips -v2 -pfs 'ike=aes_gcm'
-	AES_GCM_16-HMAC_SHA2_256-MODP2048
-	AES_GCM_16-HMAC_SHA2_512-MODP2048
-	AES_GCM_16-HMAC_SHA1-MODP2048
+	AES_GCM_16-HMAC_SHA2_512+HMAC_SHA2_256-MODP2048+MODP3072+MODP4096+MODP8192+DH19+DH20+DH21+DH31
 algparse -fips -v2 -pfs 'ike=aes-sha1-modp8192,aes-sha1-modp8192,aes-sha1-modp8192'
 	AES_CBC-HMAC_SHA1-MODP8192
 algparse -fips -v2 -pfs 'ike=aes;none'
@@ -381,3 +372,10 @@
 	ERROR: IKE PRF algorithm 'id2' is not recognized
 algparse -fips -v2 -pfs 'ike=aes_ccm'
 	ERROR: IKE encryption algorithm 'aes_ccm' is not supported
+algparse -fips -v2 -pfs 'ike=aes_gcm-sha1-none-modp2048'
+	ERROR: 'modp2048' unexpected
+algparse -fips -v2 -pfs 'ike=aes_gcm+aes_gcm-sha1-none-modp2048'
+	WARNING: discarding duplicate algorithm 'aes_gcm_16'
+	ERROR: 'modp2048' unexpected
+algparse -fips -v2 -pfs 'ike=aes+aes_gcm'
+	ERROR: AEAD and non-AEAD IKE encryption algorithm can not be combined
diff -Naur libreswan-3.27-orig/testing/pluto/algparse-02-fips/algparse.v2.txt libreswan-3.27/testing/pluto/algparse-02-fips/algparse.v2.txt
--- libreswan-3.27-orig/testing/pluto/algparse-02-fips/algparse.v2.txt	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/testing/pluto/algparse-02-fips/algparse.v2.txt	2019-02-15 16:50:12.778728094 -0500
@@ -1,12 +1,12 @@
 algparse -fips -v2 'esp'
-	AES_CBC-HMAC_SHA1_96
+	AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -fips -v2 'esp='
 	ERROR: String ended with invalid char, just after ""
 algparse -fips -v2 'esp=aes'
-	AES_CBC-HMAC_SHA1_96
+	AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -fips -v2 'esp=aes;modp2048'
 	WARNING: ignoring ESP DH algorithm MODP2048 as PFS policy is disabled
-	AES_CBC-HMAC_SHA1_96
+	AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -fips -v2 'esp=aes-sha1'
 	AES_CBC-HMAC_SHA1_96
 algparse -fips -v2 'esp=aes-sha1'
@@ -15,7 +15,7 @@
 	WARNING: ignoring ESP DH algorithm MODP2048 as PFS policy is disabled
 	AES_CBC-HMAC_SHA1_96
 algparse -fips -v2 'esp=aes-128'
-	AES_CBC_128-HMAC_SHA1_96
+	AES_CBC_128-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -fips -v2 'esp=aes-128-sha1'
 	AES_CBC_128-HMAC_SHA1_96
 algparse -fips -v2 'esp=aes-128-sha1'
@@ -47,7 +47,7 @@
 algparse -fips -v2 'esp=null-sha1'
 	ERROR: ESP encryption algorithm 'null' is not supported
 algparse -fips -v2 'esp=aes_cbc'
-	AES_CBC-HMAC_SHA1_96
+	AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -fips -v2 'esp=aes-sha'
 	AES_CBC-HMAC_SHA1_96
 algparse -fips -v2 'esp=aes-sha1'
@@ -63,7 +63,7 @@
 algparse -fips -v2 'esp=aes128-sha1'
 	AES_CBC_128-HMAC_SHA1_96
 algparse -fips -v2 'esp=aes128-aes_xcbc'
-	AES_CBC_128-AES_XCBC_96
+	ERROR: ESP integrity algorithm 'aes_xcbc' is not supported
 algparse -fips -v2 'esp=aes192-sha1'
 	AES_CBC_192-HMAC_SHA1_96
 algparse -fips -v2 'esp=aes256-sha1'
@@ -187,15 +187,15 @@
 algparse -fips -v2 'esp=aes_gcm_16_256-null'
 	AES_GCM_16_256-NONE
 algparse -fips -v2 'esp=aes_ctr'
-	AES_CTR-HMAC_SHA1_96
+	AES_CTR-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -fips -v2 'esp=aesctr'
-	AES_CTR-HMAC_SHA1_96
+	AES_CTR-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -fips -v2 'esp=aes_ctr128'
-	AES_CTR_128-HMAC_SHA1_96
+	AES_CTR_128-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -fips -v2 'esp=aes_ctr192'
-	AES_CTR_192-HMAC_SHA1_96
+	AES_CTR_192-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -fips -v2 'esp=aes_ctr256'
-	AES_CTR_256-HMAC_SHA1_96
+	AES_CTR_256-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -fips -v2 'esp=serpent'
 	ERROR: ESP encryption algorithm 'serpent' is not supported
 algparse -fips -v2 'esp=twofish'
@@ -203,7 +203,8 @@
 algparse -fips -v2 'esp=camellia_cbc_256-hmac_sha2_512_256;modp8192'
 	ERROR: ESP encryption algorithm 'camellia_cbc_256' is not supported
 algparse -fips -v2 'esp=null_auth_aes_gmac_256-null;modp8192'
-	ERROR: ESP encryption algorithm 'null_auth_aes_gmac_256' is not supported
+	WARNING: ignoring ESP DH algorithm MODP8192 as PFS policy is disabled
+	NULL_AUTH_AES_GMAC_256-NONE
 algparse -fips -v2 'esp=3des-sha1;modp8192'
 	WARNING: ignoring ESP DH algorithm MODP8192 as PFS policy is disabled
 	3DES_CBC-HMAC_SHA1_96
@@ -224,16 +225,16 @@
 	AES_CBC-HMAC_SHA1_96
 algparse -fips -v2 'esp=aes;none'
 	WARNING: ignoring redundant ESP DH algorithm NONE as PFS policy is disabled
-	AES_CBC-HMAC_SHA1_96
+	AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -fips -v2 'esp=aes;none,aes'
 	WARNING: ignoring redundant ESP DH algorithm NONE as PFS policy is disabled
-	AES_CBC-HMAC_SHA1_96
-	AES_CBC-HMAC_SHA1_96
+	AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+	AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -fips -v2 'esp=aes;none,aes;modp2048'
 	WARNING: ignoring redundant ESP DH algorithm NONE as PFS policy is disabled
 	WARNING: ignoring ESP DH algorithm MODP2048 as PFS policy is disabled
-	AES_CBC-HMAC_SHA1_96
-	AES_CBC-HMAC_SHA1_96
+	AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+	AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -fips -v2 'esp=aes-sha1-none'
 	WARNING: ignoring redundant ESP DH algorithm NONE as PFS policy is disabled
 	AES_CBC-HMAC_SHA1_96
@@ -328,7 +329,7 @@
 	3DES_CBC-HMAC_SHA1_96
 	3DES_CBC-HMAC_SHA2_256_128
 algparse -fips -v2 'ah'
-	HMAC_SHA1_96
+	HMAC_SHA2_512_256+HMAC_SHA2_256_128
 algparse -fips -v2 'ah='
 	ERROR: String ended with invalid char, just after ""
 algparse -fips -v2 'ah=md5'
@@ -355,7 +356,7 @@
 algparse -fips -v2 'ah=sha2_512'
 	HMAC_SHA2_512_256
 algparse -fips -v2 'ah=aes_xcbc'
-	AES_XCBC_96
+	ERROR: AH integrity algorithm 'aes_xcbc' is not supported
 algparse -fips -v2 'ah=sha2-none'
 	WARNING: ignoring redundant AH DH algorithm NONE as PFS policy is disabled
 	HMAC_SHA2_256_128
@@ -384,32 +385,23 @@
 algparse -fips -v2 'ah=ripemd'
 	ERROR: AH integrity algorithm 'ripemd' is not recognized
 algparse -fips -v2 'ike'
-	AES_CBC-HMAC_SHA2_256-MODP2048
-	AES_CBC-HMAC_SHA2_512-MODP2048
-	AES_CBC-HMAC_SHA1-MODP2048
-	3DES_CBC-HMAC_SHA2_256-MODP2048
-	3DES_CBC-HMAC_SHA2_512-MODP2048
-	3DES_CBC-HMAC_SHA1-MODP2048
+	AES_CBC+3DES_CBC-HMAC_SHA2_512+HMAC_SHA2_256-MODP2048+MODP3072+MODP4096+MODP8192+DH19+DH20+DH21+DH31
 algparse -fips -v2 'ike='
 	ERROR: String ended with invalid char, just after ""
 algparse -fips -v2 'ike=3des-sha1'
-	3DES_CBC-HMAC_SHA1-MODP2048
+	3DES_CBC-HMAC_SHA1-MODP2048+MODP3072+MODP4096+MODP8192+DH19+DH20+DH21+DH31
 algparse -fips -v2 'ike=3des-sha1'
-	3DES_CBC-HMAC_SHA1-MODP2048
+	3DES_CBC-HMAC_SHA1-MODP2048+MODP3072+MODP4096+MODP8192+DH19+DH20+DH21+DH31
 algparse -fips -v2 'ike=3des-sha1;modp1536'
 	ERROR: IKE DH algorithm 'modp1536' is not supported
 algparse -fips -v2 'ike=3des;dh21'
-	3DES_CBC-HMAC_SHA2_256-DH21
-	3DES_CBC-HMAC_SHA2_512-DH21
-	3DES_CBC-HMAC_SHA1-DH21
+	3DES_CBC-HMAC_SHA2_512+HMAC_SHA2_256-DH21
 algparse -fips -v2 'ike=3des-sha1;dh21'
 	3DES_CBC-HMAC_SHA1-DH21
 algparse -fips -v2 'ike=3des-sha1-ecp_521'
 	3DES_CBC-HMAC_SHA1-DH21
 algparse -fips -v2 'ike=aes_gcm'
-	AES_GCM_16-HMAC_SHA2_256-MODP2048
-	AES_GCM_16-HMAC_SHA2_512-MODP2048
-	AES_GCM_16-HMAC_SHA1-MODP2048
+	AES_GCM_16-HMAC_SHA2_512+HMAC_SHA2_256-MODP2048+MODP3072+MODP4096+MODP8192+DH19+DH20+DH21+DH31
 algparse -fips -v2 'ike=aes-sha1-modp8192,aes-sha1-modp8192,aes-sha1-modp8192'
 	AES_CBC-HMAC_SHA1-MODP8192
 algparse -fips -v2 'ike=aes;none'
@@ -420,3 +412,10 @@
 	ERROR: IKE PRF algorithm 'id2' is not recognized
 algparse -fips -v2 'ike=aes_ccm'
 	ERROR: IKE encryption algorithm 'aes_ccm' is not supported
+algparse -fips -v2 'ike=aes_gcm-sha1-none-modp2048'
+	ERROR: 'modp2048' unexpected
+algparse -fips -v2 'ike=aes_gcm+aes_gcm-sha1-none-modp2048'
+	WARNING: discarding duplicate algorithm 'aes_gcm_16'
+	ERROR: 'modp2048' unexpected
+algparse -fips -v2 'ike=aes+aes_gcm'
+	ERROR: AEAD and non-AEAD IKE encryption algorithm can not be combined
diff -Naur libreswan-3.27-orig/testing/pluto/algparse-02-fips/algparse.v.txt libreswan-3.27/testing/pluto/algparse-02-fips/algparse.v.txt
--- libreswan-3.27-orig/testing/pluto/algparse-02-fips/algparse.v.txt	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/testing/pluto/algparse-02-fips/algparse.v.txt	2019-02-15 16:32:29.006835593 -0500
@@ -3,13 +3,14 @@
 algparse: Encryption algorithm SERPENT_CBC disabled; not FIPS compliant
 algparse: Encryption algorithm TWOFISH_CBC disabled; not FIPS compliant
 algparse: Encryption algorithm TWOFISH_SSH disabled; not FIPS compliant
-algparse: Encryption algorithm NULL_AUTH_AES_GMAC disabled; not FIPS compliant
 algparse: Encryption algorithm NULL disabled; not FIPS compliant
 algparse: Encryption algorithm CHACHA20_POLY1305 disabled; not FIPS compliant
 algparse: Hash algorithm MD5 disabled; not FIPS compliant
 algparse: PRF algorithm HMAC_MD5 disabled; not FIPS compliant
+algparse: PRF algorithm AES_XCBC disabled; not FIPS compliant
 algparse: Integrity algorithm HMAC_MD5_96 disabled; not FIPS compliant
 algparse: Integrity algorithm HMAC_SHA2_256_TRUNCBUG disabled; not FIPS compliant
+algparse: Integrity algorithm AES_XCBC_96 disabled; not FIPS compliant
 algparse: DH algorithm MODP1024 disabled; not FIPS compliant
 algparse: DH algorithm MODP1536 disabled; not FIPS compliant
 algparse: DH algorithm DH31 disabled; not FIPS compliant
@@ -23,6 +24,7 @@
 algparse:   AES_GCM_8               IKEv1:     ESP     IKEv2: IKE ESP     FIPS  {256,192,*128}  aes_gcm_a
 algparse:   AES_CTR                 IKEv1: IKE ESP     IKEv2: IKE ESP     FIPS  {256,192,*128}  aesctr
 algparse:   AES_CBC                 IKEv1: IKE ESP     IKEv2: IKE ESP     FIPS  {256,192,*128}  aes
+algparse:   NULL_AUTH_AES_GMAC      IKEv1:     ESP     IKEv2:     ESP     FIPS  {256,192,*128}  aes_gmac
 algparse: FIPS Hash algorithms:
 algparse:   SHA1                    IKEv1: IKE         IKEv2:             FIPS  sha
 algparse:   SHA2_256                IKEv1: IKE         IKEv2:             FIPS  sha2, sha256
@@ -33,15 +35,13 @@
 algparse:   HMAC_SHA2_256           IKEv1: IKE         IKEv2: IKE         FIPS  sha2, sha256, sha2_256
 algparse:   HMAC_SHA2_384           IKEv1: IKE         IKEv2: IKE         FIPS  sha384, sha2_384
 algparse:   HMAC_SHA2_512           IKEv1: IKE         IKEv2: IKE         FIPS  sha512, sha2_512
-algparse:   AES_XCBC                IKEv1:             IKEv2: IKE         FIPS  aes128_xcbc
 algparse: FIPS Integrity algorithms:
 algparse:   HMAC_SHA1_96            IKEv1: IKE ESP AH  IKEv2: IKE ESP AH  FIPS  sha, sha1, sha1_96, hmac_sha1
-algparse:   HMAC_SHA2_512_256       IKEv1: IKE ESP AH  IKEv2: IKE ESP AH  FIPS  sha512, sha2_512, hmac_sha2_512
-algparse:   HMAC_SHA2_384_192       IKEv1: IKE ESP AH  IKEv2: IKE ESP AH  FIPS  sha384, sha2_384, hmac_sha2_384
-algparse:   HMAC_SHA2_256_128       IKEv1: IKE ESP AH  IKEv2: IKE ESP AH  FIPS  sha2, sha256, sha2_256, hmac_sha2_256
-algparse:   AES_XCBC_96             IKEv1:     ESP AH  IKEv2: IKE ESP AH  FIPS  aes_xcbc, aes128_xcbc, aes128_xcbc_96
+algparse:   HMAC_SHA2_512_256       IKEv1: IKE ESP AH  IKEv2: IKE ESP AH  FIPS  sha512, sha2_512, sha2_512_256, hmac_sha2_512
+algparse:   HMAC_SHA2_384_192       IKEv1: IKE ESP AH  IKEv2: IKE ESP AH  FIPS  sha384, sha2_384, sha2_384_192, hmac_sha2_384
+algparse:   HMAC_SHA2_256_128       IKEv1: IKE ESP AH  IKEv2: IKE ESP AH  FIPS  sha2, sha256, sha2_256, sha2_256_128, hmac_sha2_256
 algparse:   AES_CMAC_96             IKEv1:     ESP AH  IKEv2:     ESP AH  FIPS  aes_cmac
-algparse:   NONE                    IKEv1:     ESP     IKEv2:     ESP     FIPS  null
+algparse:   NONE                    IKEv1:     ESP     IKEv2: IKE ESP     FIPS  null
 algparse: FIPS DH algorithms:
 algparse:   NONE                    IKEv1:             IKEv2: IKE ESP AH  FIPS  null, dh0
 algparse:   MODP2048                IKEv1: IKE ESP AH  IKEv2: IKE ESP AH  FIPS  dh14
@@ -49,7 +49,7 @@
 algparse:   MODP4096                IKEv1: IKE ESP AH  IKEv2: IKE ESP AH  FIPS  dh16
 algparse:   MODP6144                IKEv1: IKE ESP AH  IKEv2: IKE ESP AH  FIPS  dh17
 algparse:   MODP8192                IKEv1: IKE ESP AH  IKEv2: IKE ESP AH  FIPS  dh18
-algparse:   DH19                    IKEv1: IKE         IKEv2: IKE ESP AH  FIPS  ecp_256
-algparse:   DH20                    IKEv1: IKE         IKEv2: IKE ESP AH  FIPS  ecp_384
-algparse:   DH21                    IKEv1: IKE         IKEv2: IKE ESP AH  FIPS  ecp_521
+algparse:   DH19                    IKEv1: IKE         IKEv2: IKE ESP AH  FIPS  ecp_256, ecp256
+algparse:   DH20                    IKEv1: IKE         IKEv2: IKE ESP AH  FIPS  ecp_384, ecp384
+algparse:   DH21                    IKEv1: IKE         IKEv2: IKE ESP AH  FIPS  ecp_521, ecp521
 algparse: leak detective found no leaks
diff -Naur libreswan-3.27-orig/testing/pluto/algparse-02-fips/west.conf libreswan-3.27/testing/pluto/algparse-02-fips/west.conf
--- libreswan-3.27-orig/testing/pluto/algparse-02-fips/west.conf	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/testing/pluto/algparse-02-fips/west.conf	2019-02-15 16:32:29.006835593 -0500
@@ -10,4 +10,7 @@
 	protostack=netkey
 	plutodebug=all
 
+conn %default
+	ikev2=no
+
 include	/testing/baseconfigs/all/etc/ipsec.d/ipsec.conf.common
diff -Naur libreswan-3.27-orig/testing/pluto/algparse-02-fips/west.console.txt libreswan-3.27/testing/pluto/algparse-02-fips/west.console.txt
--- libreswan-3.27-orig/testing/pluto/algparse-02-fips/west.console.txt	2018-10-07 22:52:09.000000000 -0400
+++ libreswan-3.27/testing/pluto/algparse-02-fips/west.console.txt	2019-02-15 16:32:29.006835593 -0500
@@ -7,8 +7,6 @@
  ../bin/algparse.sh PATH/libexec/ipsec/algparse algparse*.txt
 PATH/libexec/ipsec/algparse -v1 -pfs -t # algparse.v1.pfs.txt
 PATH/libexec/ipsec/algparse -v1 -t # algparse.v1.txt
-PATH/libexec/ipsec/algparse -v1 -v2 -pfs -t # algparse.v1.v2.pfs.txt
-PATH/libexec/ipsec/algparse -v1 -v2 -t # algparse.v1.v2.txt
 PATH/libexec/ipsec/algparse -v2 -pfs -t # algparse.v2.pfs.txt
 PATH/libexec/ipsec/algparse -v2 -t # algparse.v2.txt
 PATH/libexec/ipsec/algparse -v # algparse.v.txt
@@ -50,6 +48,7 @@
   AES_GCM_8               IKEv1:     ESP     IKEv2: IKE ESP     FIPS  {256,192,*128}  aes_gcm_a
   AES_CTR                 IKEv1: IKE ESP     IKEv2: IKE ESP     FIPS  {256,192,*128}  aesctr
   AES_CBC                 IKEv1: IKE ESP     IKEv2: IKE ESP     FIPS  {256,192,*128}  aes
+  NULL_AUTH_AES_GMAC      IKEv1:     ESP     IKEv2:     ESP     FIPS  {256,192,*128}  aes_gmac
 FIPS Hash algorithms:
   SHA1                    IKEv1: IKE         IKEv2:             FIPS  sha
   SHA2_256                IKEv1: IKE         IKEv2:             FIPS  sha2, sha256
@@ -60,13 +59,11 @@
   HMAC_SHA2_256           IKEv1: IKE         IKEv2: IKE         FIPS  sha2, sha256, sha2_256
   HMAC_SHA2_384           IKEv1: IKE         IKEv2: IKE         FIPS  sha384, sha2_384
   HMAC_SHA2_512           IKEv1: IKE         IKEv2: IKE         FIPS  sha512, sha2_512
-  AES_XCBC                IKEv1:             IKEv2: IKE         FIPS  aes128_xcbc
 FIPS Integrity algorithms:
   HMAC_SHA1_96            IKEv1: IKE ESP AH  IKEv2: IKE ESP AH  FIPS  sha, sha1, sha1_96, hmac_sha1
   HMAC_SHA2_512_256       IKEv1: IKE ESP AH  IKEv2: IKE ESP AH  FIPS  sha512, sha2_512, hmac_sha2_512
   HMAC_SHA2_384_192       IKEv1: IKE ESP AH  IKEv2: IKE ESP AH  FIPS  sha384, sha2_384, hmac_sha2_384
   HMAC_SHA2_256_128       IKEv1: IKE ESP AH  IKEv2: IKE ESP AH  FIPS  sha2, sha256, sha2_256, hmac_sha2_256
-  AES_XCBC_96             IKEv1:     ESP AH  IKEv2: IKE ESP AH  FIPS  aes_xcbc, aes128_xcbc, aes128_xcbc_96
   AES_CMAC_96             IKEv1:     ESP AH  IKEv2:     ESP AH  FIPS  aes_cmac
   NONE                    IKEv1:     ESP     IKEv2:     ESP     FIPS  null
 FIPS DH algorithms: