Require configured groups for key exchange to be unique

There is no reason for the list of supported groups for key exchange to
contain duplicates, because a group is either supported or not, and
specifying it multiple times has no additional effect. Also it is a
syntax error on the wire. Allowing duplicates in the SSL_CTX and
SSL_CONFIG members that track this list would create problems because we
will soon require other inputs (explicitly configured client key shares)
to be a subset of the supported groups. This CL requires the groups
configured via SSL_{,CTX_}set1_{groups,group_ids,groups_list} to be
unique.

Update-Note: The setters for supported groups,
SSL_{,CTX_}set1_{groups,group_ids,groups_list} will now fail if the
provided list of groups contains duplicates.

Change-Id: I67aa3bac811da6239e8f89fe501218a7a60dba11
Bug: 437414371
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/81467
Auto-Submit: Lily Chen <chlily@google.com>
Reviewed-by: David Benjamin <davidben@google.com>
Commit-Queue: Lily Chen <chlily@google.com>
diff --git a/crypto/err/ssl.errordata b/crypto/err/ssl.errordata
index 7d77cfd..01c4ca6 100644
--- a/crypto/err/ssl.errordata
+++ b/crypto/err/ssl.errordata
@@ -54,6 +54,7 @@
 SSL,254,DOWNGRADE_DETECTED
 SSL,143,DTLS_MESSAGE_TOO_BIG
 SSL,257,DUPLICATE_EXTENSION
+SSL,330,DUPLICATE_GROUP
 SSL,264,DUPLICATE_KEY_SHARE
 SSL,296,DUPLICATE_SIGNATURE_ALGORITHM
 SSL,283,EARLY_DATA_NOT_IN_USE
@@ -83,6 +84,7 @@
 SSL,259,INVALID_ALPN_PROTOCOL
 SSL,315,INVALID_ALPN_PROTOCOL_LIST
 SSL,322,INVALID_ALPS_CODEPOINT
+SSL,329,INVALID_CERTIFICATE_PROPERTY_LIST
 SSL,314,INVALID_CLIENT_HELLO_INNER
 SSL,158,INVALID_COMMAND
 SSL,256,INVALID_COMPRESSION_LIST
@@ -97,6 +99,7 @@
 SSL,324,INVALID_SPAKE2PLUSV1_VALUE
 SSL,160,INVALID_SSL_SESSION
 SSL,161,INVALID_TICKET_KEYS_LENGTH
+SSL,328,INVALID_TRUST_ANCHOR_LIST
 SSL,302,KEY_USAGE_BIT_INCORRECT
 SSL,162,LENGTH_MISMATCH
 SSL,164,MISSING_EXTENSION
@@ -243,8 +246,6 @@
 SSL,237,UNSUPPORTED_CIPHER
 SSL,238,UNSUPPORTED_COMPRESSION_ALGORITHM
 SSL,327,UNSUPPORTED_CREDENTIAL_LIST
-SSL,328,INVALID_TRUST_ANCHOR_LIST
-SSL,329,INVALID_CERTIFICATE_PROPERTY_LIST
 SSL,312,UNSUPPORTED_ECH_SERVER_CONFIG
 SSL,239,UNSUPPORTED_ELLIPTIC_CURVE
 SSL,240,UNSUPPORTED_PROTOCOL
diff --git a/gen/crypto/err_data.cc b/gen/crypto/err_data.cc
index 00b3216..6048a69 100644
--- a/gen/crypto/err_data.cc
+++ b/gen/crypto/err_data.cc
@@ -199,51 +199,51 @@
     0x283500f7,
     0x28358cc1,
     0x2836099a,
-    0x2c3233f4,
+    0x2c323404,
     0x2c3293de,
-    0x2c333402,
-    0x2c33b414,
-    0x2c343428,
-    0x2c34b43a,
-    0x2c353455,
-    0x2c35b467,
-    0x2c363497,
+    0x2c333412,
+    0x2c33b424,
+    0x2c343438,
+    0x2c34b44a,
+    0x2c353465,
+    0x2c35b477,
+    0x2c3634a7,
     0x2c36833a,
-    0x2c3734a4,
-    0x2c37b4d0,
-    0x2c38350e,
-    0x2c38b525,
-    0x2c393543,
-    0x2c39b553,
-    0x2c3a3565,
-    0x2c3ab579,
-    0x2c3b358a,
-    0x2c3bb5a9,
+    0x2c3734b4,
+    0x2c37b4e0,
+    0x2c38351e,
+    0x2c38b535,
+    0x2c393553,
+    0x2c39b563,
+    0x2c3a3575,
+    0x2c3ab589,
+    0x2c3b359a,
+    0x2c3bb5b9,
     0x2c3c13f0,
     0x2c3c9406,
-    0x2c3d35ee,
+    0x2c3d35fe,
     0x2c3d941f,
-    0x2c3e3618,
-    0x2c3eb626,
-    0x2c3f363e,
-    0x2c3fb656,
-    0x2c403680,
+    0x2c3e3628,
+    0x2c3eb636,
+    0x2c3f364e,
+    0x2c3fb666,
+    0x2c403690,
     0x2c4092d3,
-    0x2c413691,
-    0x2c41b6a4,
+    0x2c4136a1,
+    0x2c41b6b4,
     0x2c421299,
-    0x2c42b6b5,
+    0x2c42b6c5,
     0x2c43076d,
-    0x2c43b59b,
-    0x2c4434e3,
-    0x2c44b663,
-    0x2c45347a,
-    0x2c45b4b6,
-    0x2c463533,
-    0x2c46b5bd,
-    0x2c4735d2,
-    0x2c47b60b,
-    0x2c4834f5,
+    0x2c43b5ab,
+    0x2c4434f3,
+    0x2c44b673,
+    0x2c45348a,
+    0x2c45b4c6,
+    0x2c463543,
+    0x2c46b5cd,
+    0x2c4735e2,
+    0x2c47b61b,
+    0x2c483505,
     0x30320000,
     0x30328015,
     0x3033001f,
@@ -427,224 +427,225 @@
     0x40469e4b,
     0x40471e59,
     0x40479e80,
-    0x40481ef1,
-    0x40489fab,
-    0x40491fc2,
-    0x40499fdc,
-    0x404a1ff3,
-    0x404aa011,
-    0x404b2029,
-    0x404ba056,
-    0x404c206c,
-    0x404ca07e,
-    0x404d209f,
-    0x404da0d8,
-    0x404e20ec,
-    0x404ea0f9,
-    0x404f21aa,
-    0x404fa220,
-    0x405022aa,
-    0x4050a2be,
-    0x405122f1,
-    0x40522301,
-    0x4052a325,
-    0x4053233d,
-    0x4053a350,
-    0x40542365,
-    0x4054a388,
-    0x405523b3,
-    0x4055a3f0,
-    0x40562415,
-    0x4056a42e,
-    0x40572446,
-    0x4057a459,
-    0x4058246e,
-    0x4058a495,
-    0x405924c4,
-    0x4059a504,
-    0x405aa518,
-    0x405b2530,
-    0x405ba541,
-    0x405c2554,
-    0x405ca593,
-    0x405d25a0,
-    0x405da5c5,
-    0x405e2603,
+    0x40481f01,
+    0x40489fbb,
+    0x40491fd2,
+    0x40499fec,
+    0x404a2003,
+    0x404aa021,
+    0x404b2039,
+    0x404ba066,
+    0x404c207c,
+    0x404ca08e,
+    0x404d20af,
+    0x404da0e8,
+    0x404e20fc,
+    0x404ea109,
+    0x404f21dc,
+    0x404fa252,
+    0x405022dc,
+    0x4050a2f0,
+    0x4051233d,
+    0x4052234d,
+    0x4052a371,
+    0x40532389,
+    0x4053a39c,
+    0x405423b1,
+    0x4054a3d4,
+    0x405523ff,
+    0x4055a43c,
+    0x40562461,
+    0x4056a47a,
+    0x40572492,
+    0x4057a4a5,
+    0x405824ba,
+    0x4058a4e1,
+    0x40592510,
+    0x4059a550,
+    0x405aa564,
+    0x405b257c,
+    0x405ba58d,
+    0x405c25a0,
+    0x405ca5df,
+    0x405d25ec,
+    0x405da611,
+    0x405e264f,
     0x405e8afe,
-    0x405f2652,
-    0x405fa65f,
-    0x4060266d,
-    0x4060a68f,
-    0x40612703,
-    0x4061a73b,
-    0x40622752,
-    0x4062a763,
-    0x406327b0,
-    0x4063a7c5,
-    0x406427dc,
-    0x4064a808,
-    0x40652823,
-    0x4065a83a,
-    0x40662852,
-    0x4066a87c,
-    0x406728a7,
-    0x4067a8ec,
-    0x40682934,
-    0x4068a955,
-    0x40692987,
-    0x4069a9b5,
-    0x406a29d6,
-    0x406aa9f6,
-    0x406b2b7e,
-    0x406baba1,
-    0x406c2bb7,
-    0x406caec1,
-    0x406d2ef0,
-    0x406daf18,
-    0x406e2f46,
-    0x406eaf93,
-    0x406f2fec,
-    0x406fb024,
-    0x40703037,
-    0x4070b054,
+    0x405f269e,
+    0x405fa6ab,
+    0x406026b9,
+    0x4060a6db,
+    0x4061274f,
+    0x4061a787,
+    0x4062279e,
+    0x4062a7af,
+    0x406327fc,
+    0x4063a811,
+    0x40642828,
+    0x4064a854,
+    0x4065286f,
+    0x4065a886,
+    0x4066289e,
+    0x4066a8c8,
+    0x406728f3,
+    0x4067a938,
+    0x40682980,
+    0x4068a9a1,
+    0x406929d3,
+    0x4069aa01,
+    0x406a2a22,
+    0x406aaa42,
+    0x406b2bca,
+    0x406babed,
+    0x406c2c03,
+    0x406caf0d,
+    0x406d2f3c,
+    0x406daf64,
+    0x406e2f92,
+    0x406eafdf,
+    0x406f3038,
+    0x406fb070,
+    0x40703083,
+    0x4070b0a0,
     0x4071084d,
-    0x4071b066,
-    0x40723079,
-    0x4072b0af,
-    0x407330c7,
+    0x4071b0b2,
+    0x407230c5,
+    0x4072b0fb,
+    0x40733113,
     0x40739608,
-    0x407430db,
-    0x4074b0f5,
-    0x40753106,
-    0x4075b11a,
-    0x40763128,
+    0x40743127,
+    0x4074b141,
+    0x40753152,
+    0x4075b166,
+    0x40763174,
     0x40769396,
-    0x4077314d,
-    0x4077b1e5,
-    0x40783200,
-    0x4078b239,
-    0x40793250,
-    0x4079b266,
-    0x407a3292,
-    0x407ab2a5,
-    0x407b32ba,
-    0x407bb2cc,
-    0x407c32fd,
-    0x407cb306,
-    0x407d2970,
-    0x407da248,
-    0x407e3215,
-    0x407ea4a5,
+    0x40773199,
+    0x4077b1f5,
+    0x40783210,
+    0x4078b249,
+    0x40793260,
+    0x4079b276,
+    0x407a32a2,
+    0x407ab2b5,
+    0x407b32ca,
+    0x407bb2dc,
+    0x407c330d,
+    0x407cb316,
+    0x407d29bc,
+    0x407da27a,
+    0x407e3225,
+    0x407ea4f1,
     0x407f1e6d,
-    0x407fa040,
-    0x408021ba,
+    0x407fa050,
+    0x408021ec,
     0x40809e95,
-    0x40812313,
-    0x4081a147,
-    0x40822f31,
+    0x4081235f,
+    0x4081a157,
+    0x40822f7d,
     0x40829be8,
-    0x40832480,
-    0x4083a7ed,
-    0x40841ea9,
-    0x4084a4dd,
-    0x40852565,
-    0x4085a6ca,
-    0x408625e5,
-    0x4086a262,
-    0x40872f77,
-    0x4087a718,
+    0x408324cc,
+    0x4083a839,
+    0x40841eb9,
+    0x4084a529,
+    0x408525b1,
+    0x4085a716,
+    0x40862631,
+    0x4086a294,
+    0x40872fc3,
+    0x4087a764,
     0x40881c26,
-    0x4088a8ff,
+    0x4088a94b,
     0x40891c75,
     0x40899c02,
-    0x408a2bef,
+    0x408a2c3b,
     0x408a9a20,
-    0x408b32e1,
-    0x408bb001,
-    0x408c2575,
-    0x408d1f91,
-    0x408d9edb,
-    0x408e20c1,
-    0x408ea3d0,
-    0x408f2913,
-    0x408fa6e6,
-    0x409028c8,
-    0x4090a5b7,
-    0x40912bd7,
+    0x408b32f1,
+    0x408bb04d,
+    0x408c25c1,
+    0x408d1fa1,
+    0x408d9eeb,
+    0x408e20d1,
+    0x408ea41c,
+    0x408f295f,
+    0x408fa732,
+    0x40902914,
+    0x4090a603,
+    0x40912c23,
     0x40919a58,
     0x40921cc2,
-    0x4092afb2,
-    0x40933092,
-    0x4093a273,
-    0x40941ebd,
-    0x4094ac08,
-    0x40952774,
-    0x4095b272,
-    0x40962f5e,
-    0x4096a1d3,
-    0x409722d9,
-    0x4097a110,
+    0x4092affe,
+    0x409330de,
+    0x4093a2a5,
+    0x40941ecd,
+    0x4094ac54,
+    0x409527c0,
+    0x4095b282,
+    0x40962faa,
+    0x4096a205,
+    0x40972325,
+    0x4097a120,
     0x40981d22,
-    0x4098a788,
-    0x40992fce,
-    0x4099a3fd,
-    0x409a2396,
+    0x4098a7d4,
+    0x4099301a,
+    0x4099a449,
+    0x409a23e2,
     0x409a9a3c,
-    0x409b1f17,
-    0x409b9f42,
-    0x409c31c7,
-    0x409c9f6a,
-    0x409d218f,
-    0x409da15d,
+    0x409b1f27,
+    0x409b9f52,
+    0x409c31d7,
+    0x409c9f7a,
+    0x409d21c1,
+    0x409da16d,
     0x409e1db3,
-    0x409ea208,
-    0x409f21f0,
-    0x409f9f0a,
-    0x40a02230,
-    0x40a0a12a,
-    0x40a12178,
-    0x40a1a4f1,
-    0x40a2228f,
-    0x40a2a643,
-    0x40a326b7,
-    0x40a3b16f,
-    0x40a4318b,
-    0x40a4b1a5,
-    0x41f42aa9,
-    0x41f92b3b,
-    0x41fe2a2e,
-    0x41feace4,
-    0x41ff2e12,
-    0x42032ac2,
-    0x42082ae4,
-    0x4208ab20,
-    0x42092a12,
-    0x4209ab5a,
-    0x420a2a69,
-    0x420aaa49,
-    0x420b2a89,
-    0x420bab02,
-    0x420c2e2e,
-    0x420cac18,
-    0x420d2ccb,
-    0x420dad02,
-    0x42122d35,
-    0x42172df5,
-    0x4217ad77,
-    0x421c2d99,
-    0x421f2d54,
-    0x42212ea6,
-    0x42262dd8,
-    0x422b2e84,
-    0x422baca6,
-    0x422c2e66,
-    0x422cac59,
-    0x422d2c32,
-    0x422dae45,
-    0x422e2c85,
-    0x42302db4,
-    0x4230ad1c,
-    0x42312624,
+    0x409ea23a,
+    0x409f2222,
+    0x409f9f1a,
+    0x40a02262,
+    0x40a0a13a,
+    0x40a12188,
+    0x40a1a53d,
+    0x40a222c1,
+    0x40a2a68f,
+    0x40a32703,
+    0x40a3b1bb,
+    0x40a4230b,
+    0x40a4a19f,
+    0x40a51ea9,
+    0x41f42af5,
+    0x41f92b87,
+    0x41fe2a7a,
+    0x41fead30,
+    0x41ff2e5e,
+    0x42032b0e,
+    0x42082b30,
+    0x4208ab6c,
+    0x42092a5e,
+    0x4209aba6,
+    0x420a2ab5,
+    0x420aaa95,
+    0x420b2ad5,
+    0x420bab4e,
+    0x420c2e7a,
+    0x420cac64,
+    0x420d2d17,
+    0x420dad4e,
+    0x42122d81,
+    0x42172e41,
+    0x4217adc3,
+    0x421c2de5,
+    0x421f2da0,
+    0x42212ef2,
+    0x42262e24,
+    0x422b2ed0,
+    0x422bacf2,
+    0x422c2eb2,
+    0x422caca5,
+    0x422d2c7e,
+    0x422dae91,
+    0x422e2cd1,
+    0x42302e00,
+    0x4230ad68,
+    0x42312670,
     0x44320778,
     0x44328787,
     0x44330793,
@@ -700,71 +701,71 @@
     0x4c4194e8,
     0x4c421651,
     0x4c429430,
-    0x503236c7,
-    0x5032b6d6,
-    0x503336e1,
-    0x5033b6f1,
-    0x5034370a,
-    0x5034b724,
-    0x50353732,
-    0x5035b748,
-    0x5036375a,
-    0x5036b770,
-    0x50373789,
-    0x5037b79c,
-    0x503837b4,
-    0x5038b7c5,
-    0x503937da,
-    0x5039b7ee,
-    0x503a380e,
-    0x503ab824,
-    0x503b383c,
-    0x503bb84e,
-    0x503c386a,
-    0x503cb881,
-    0x503d389a,
-    0x503db8b0,
-    0x503e38bd,
-    0x503eb8d3,
-    0x503f38e5,
+    0x503236d7,
+    0x5032b6e6,
+    0x503336f1,
+    0x5033b701,
+    0x5034371a,
+    0x5034b734,
+    0x50353742,
+    0x5035b758,
+    0x5036376a,
+    0x5036b780,
+    0x50373799,
+    0x5037b7ac,
+    0x503837c4,
+    0x5038b7d5,
+    0x503937ea,
+    0x5039b7fe,
+    0x503a381e,
+    0x503ab834,
+    0x503b384c,
+    0x503bb85e,
+    0x503c387a,
+    0x503cb891,
+    0x503d38aa,
+    0x503db8c0,
+    0x503e38cd,
+    0x503eb8e3,
+    0x503f38f5,
     0x503f83b3,
-    0x504038f8,
-    0x5040b908,
-    0x50413922,
-    0x5041b931,
-    0x5042394b,
-    0x5042b968,
-    0x50433978,
-    0x5043b988,
-    0x504439a5,
+    0x50403908,
+    0x5040b918,
+    0x50413932,
+    0x5041b941,
+    0x5042395b,
+    0x5042b978,
+    0x50433988,
+    0x5043b998,
+    0x504439b5,
     0x50448469,
-    0x504539b9,
-    0x5045b9d7,
-    0x504639ea,
-    0x5046ba00,
-    0x50473a12,
-    0x5047ba27,
-    0x50483a4d,
-    0x5048ba5b,
-    0x50493a6e,
-    0x5049ba83,
-    0x504a3a99,
-    0x504abaa9,
-    0x504b3ac9,
-    0x504bbadc,
-    0x504c3aff,
-    0x504cbb2d,
-    0x504d3b5a,
-    0x504dbb77,
-    0x504e3b92,
-    0x504ebbae,
-    0x504f3bc0,
-    0x504fbbd7,
-    0x50503be6,
+    0x504539c9,
+    0x5045b9e7,
+    0x504639fa,
+    0x5046ba10,
+    0x50473a22,
+    0x5047ba37,
+    0x50483a5d,
+    0x5048ba6b,
+    0x50493a7e,
+    0x5049ba93,
+    0x504a3aa9,
+    0x504abab9,
+    0x504b3ad9,
+    0x504bbaec,
+    0x504c3b0f,
+    0x504cbb3d,
+    0x504d3b6a,
+    0x504dbb87,
+    0x504e3ba2,
+    0x504ebbbe,
+    0x504f3bd0,
+    0x504fbbe7,
+    0x50503bf6,
     0x50508729,
-    0x50513bf9,
-    0x5051b997,
-    0x50523b3f,
+    0x50513c09,
+    0x5051b9a7,
+    0x50523b4f,
     0x58321011,
     0x68320fd3,
     0x68328d2b,
@@ -809,19 +810,19 @@
     0x7c3212af,
     0x803214fb,
     0x80328090,
-    0x803333c3,
+    0x803333d3,
     0x803380b9,
-    0x803433d2,
-    0x8034b33a,
-    0x80353358,
-    0x8035b3e6,
-    0x8036339a,
-    0x8036b349,
-    0x8037338c,
-    0x8037b327,
-    0x803833ad,
-    0x8038b369,
-    0x8039337e,
+    0x803433e2,
+    0x8034b34a,
+    0x80353368,
+    0x8035b3f6,
+    0x803633aa,
+    0x8036b359,
+    0x8037339c,
+    0x8037b337,
+    0x803833bd,
+    0x8038b379,
+    0x8039338e,
     0x84320bb0,
     0x84328bc9,
 };
@@ -1221,6 +1222,7 @@
     "DOWNGRADE_DETECTED\0"
     "DTLS_MESSAGE_TOO_BIG\0"
     "DUPLICATE_EXTENSION\0"
+    "DUPLICATE_GROUP\0"
     "DUPLICATE_KEY_SHARE\0"
     "DUPLICATE_SIGNATURE_ALGORITHM\0"
     "EARLY_DATA_NOT_IN_USE\0"
@@ -1250,6 +1252,7 @@
     "INVALID_ALPN_PROTOCOL\0"
     "INVALID_ALPN_PROTOCOL_LIST\0"
     "INVALID_ALPS_CODEPOINT\0"
+    "INVALID_CERTIFICATE_PROPERTY_LIST\0"
     "INVALID_CLIENT_HELLO_INNER\0"
     "INVALID_COMMAND\0"
     "INVALID_COMPRESSION_LIST\0"
@@ -1264,6 +1267,7 @@
     "INVALID_SPAKE2PLUSV1_VALUE\0"
     "INVALID_SSL_SESSION\0"
     "INVALID_TICKET_KEYS_LENGTH\0"
+    "INVALID_TRUST_ANCHOR_LIST\0"
     "KEY_USAGE_BIT_INCORRECT\0"
     "LENGTH_MISMATCH\0"
     "MISSING_EXTENSION\0"
@@ -1406,8 +1410,6 @@
     "UNSAFE_LEGACY_RENEGOTIATION_DISABLED\0"
     "UNSUPPORTED_COMPRESSION_ALGORITHM\0"
     "UNSUPPORTED_CREDENTIAL_LIST\0"
-    "INVALID_TRUST_ANCHOR_LIST\0"
-    "INVALID_CERTIFICATE_PROPERTY_LIST\0"
     "UNSUPPORTED_ECH_SERVER_CONFIG\0"
     "UNSUPPORTED_ELLIPTIC_CURVE\0"
     "UNSUPPORTED_PROTOCOL\0"
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 9ee7ec1..cef9884 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -2531,15 +2531,15 @@
 #define SSL_GROUP_MLKEM1024 0x0202
 
 // SSL_CTX_set1_group_ids sets the preferred groups for |ctx| to |group_ids|.
-// Each element of |group_ids| should be one of the |SSL_GROUP_*| constants. It
-// returns one on success and zero on failure.
+// Each element of |group_ids| should be a unique one of the |SSL_GROUP_*|
+// constants. It returns one on success and zero on failure.
 OPENSSL_EXPORT int SSL_CTX_set1_group_ids(SSL_CTX *ctx,
                                           const uint16_t *group_ids,
                                           size_t num_group_ids);
 
 // SSL_set1_group_ids sets the preferred groups for |ssl| to |group_ids|. Each
-// element of |group_ids| should be one of the |SSL_GROUP_*| constants. It
-// returns one on success and zero on failure.
+// element of |group_ids| should be a unique one of the |SSL_GROUP_*| constants.
+// It returns one on success and zero on failure.
 OPENSSL_EXPORT int SSL_set1_group_ids(SSL *ssl, const uint16_t *group_ids,
                                       size_t num_group_ids);
 
@@ -2573,25 +2573,27 @@
 // library.
 
 // SSL_CTX_set1_groups sets the preferred groups for |ctx| to be |groups|. Each
-// element of |groups| should be a |NID_*| constant from nid.h. It returns one
-// on success and zero on failure.
+// element of |groups| should be a unique |NID_*| constant from nid.h. It
+// returns one on success and zero on failure.
 OPENSSL_EXPORT int SSL_CTX_set1_groups(SSL_CTX *ctx, const int *groups,
                                        size_t num_groups);
 
 // SSL_set1_groups sets the preferred groups for |ssl| to be |groups|. Each
-// element of |groups| should be a |NID_*| constant from nid.h. It returns one
-// on success and zero on failure.
+// element of |groups| should be a unique |NID_*| constant from nid.h. It
+// returns one on success and zero on failure.
 OPENSSL_EXPORT int SSL_set1_groups(SSL *ssl, const int *groups,
                                    size_t num_groups);
 
 // SSL_CTX_set1_groups_list decodes |groups| as a colon-separated list of group
 // names (e.g. "X25519" or "P-256") and sets |ctx|'s preferred groups to the
-// result. It returns one on success and zero on failure.
+// result. The list must not contain duplicates. It returns one on success and
+// zero on failure.
 OPENSSL_EXPORT int SSL_CTX_set1_groups_list(SSL_CTX *ctx, const char *groups);
 
 // SSL_set1_groups_list decodes |groups| as a colon-separated list of group
 // names (e.g. "X25519" or "P-256") and sets |ssl|'s preferred groups to the
-// result. It returns one on success and zero on failure.
+// result. The list must not contain duplicates. It returns one on success and
+// zero on failure.
 OPENSSL_EXPORT int SSL_set1_groups_list(SSL *ssl, const char *groups);
 
 // SSL_get_negotiated_group returns the NID of the group used by |ssl|'s most
@@ -6469,6 +6471,7 @@
 #define SSL_R_UNSUPPORTED_CREDENTIAL_LIST 327
 #define SSL_R_INVALID_TRUST_ANCHOR_LIST 328
 #define SSL_R_INVALID_CERTIFICATE_PROPERTY_LIST 329
+#define SSL_R_DUPLICATE_GROUP 330
 #define SSL_R_SSLV3_ALERT_CLOSE_NOTIFY 1000
 #define SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE 1010
 #define SSL_R_SSLV3_ALERT_BAD_RECORD_MAC 1020
diff --git a/ssl/ssl_lib.cc b/ssl/ssl_lib.cc
index 24c0d49..3188317 100644
--- a/ssl/ssl_lib.cc
+++ b/ssl/ssl_lib.cc
@@ -1815,6 +1815,21 @@
   return 1;
 }
 
+static bool check_no_duplicates(Span<const uint16_t> list) {
+  if (list.size() < 2) {
+    return true;
+  }
+  for (size_t i = 0; i < list.size() - 1; ++i) {
+    for (size_t j = i + 1; j < list.size(); ++j) {
+      if (list[i] == list[j]) {
+        OPENSSL_PUT_ERROR(SSL, SSL_R_DUPLICATE_GROUP);
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
 static bool check_group_ids(Span<const uint16_t> group_ids) {
   for (uint16_t group_id : group_ids) {
     if (ssl_group_id_to_nid(group_id) == NID_undef) {
@@ -1822,7 +1837,7 @@
       return false;
     }
   }
-  return true;
+  return check_no_duplicates(group_ids);
 }
 
 int SSL_CTX_set1_group_ids(SSL_CTX *ctx, const uint16_t *group_ids,
@@ -1854,6 +1869,9 @@
       return false;
     }
   }
+  if (!check_no_duplicates(group_ids)) {
+    return false;
+  }
 
   *out_group_ids = std::move(group_ids);
   return true;
@@ -1905,6 +1923,9 @@
   } while (col);
 
   assert(i == count);
+  if (!check_no_duplicates(group_ids)) {
+    return false;
+  }
   *out_group_ids = std::move(group_ids);
   return true;
 }
diff --git a/ssl/ssl_test.cc b/ssl/ssl_test.cc
index 35711a7..84385fb 100644
--- a/ssl/ssl_test.cc
+++ b/ssl/ssl_test.cc
@@ -534,6 +534,7 @@
     "P-256:RSA",
     "X25519:P-256:",
     ":X25519:P-256",
+    "X25519:X25519",
 };
 
 static std::string CipherListToString(SSL_CTX *ctx) {
@@ -9986,6 +9987,16 @@
   static const int kInvalidNIDs[] = {NID_rsaEncryption};
   EXPECT_FALSE(
       SSL_CTX_set1_groups(ctx.get(), kInvalidNIDs, std::size(kInvalidNIDs)));
+
+  // Duplicates are not allowed.
+  static const uint16_t kDuplicateIDs[] = {SSL_GROUP_X25519_MLKEM768,
+                                           SSL_GROUP_X25519, SSL_GROUP_X25519};
+  EXPECT_FALSE(SSL_CTX_set1_group_ids(ctx.get(), kDuplicateIDs,
+                                      std::size(kDuplicateIDs)));
+  static const int kDuplicateNIDs[] = {NID_X25519, NID_X9_62_prime256v1,
+                                       NID_X25519};
+  EXPECT_FALSE(SSL_CTX_set1_groups(ctx.get(), kDuplicateNIDs,
+                                   std::size(kDuplicateNIDs)));
 }
 
 TEST(SSLTest, NameLists) {