1 package org.bouncycastle.tls;
2 
3 import java.io.IOException;
4 import java.io.InputStream;
5 import java.io.OutputStream;
6 import java.math.BigInteger;
7 
8 import org.bouncycastle.tls.crypto.DHGroup;
9 import org.bouncycastle.tls.crypto.DHStandardGroups;
10 import org.bouncycastle.tls.crypto.TlsDHConfig;
11 import org.bouncycastle.util.Arrays;
12 import org.bouncycastle.util.BigIntegers;
13 
14 public class TlsDHUtils
15 {
createNamedDHConfig(TlsContext context, int namedGroup)16     public static TlsDHConfig createNamedDHConfig(TlsContext context, int namedGroup)
17     {
18         if (namedGroup < 0 || NamedGroup.getFiniteFieldBits(namedGroup) < 1)
19         {
20             return null;
21         }
22 
23         boolean padded = TlsUtils.isTLSv13(context);
24         return new TlsDHConfig(namedGroup, padded);
25     }
26 
getDHGroup(TlsDHConfig dhConfig)27     public static DHGroup getDHGroup(TlsDHConfig dhConfig)
28     {
29         int namedGroup = dhConfig.getNamedGroup();
30         if (namedGroup >= 0)
31         {
32             return getNamedDHGroup(namedGroup);
33         }
34 
35         return dhConfig.getExplicitGroup();
36     }
37 
getNamedDHGroup(int namedGroup)38     public static DHGroup getNamedDHGroup(int namedGroup)
39     {
40         switch (namedGroup)
41         {
42         case NamedGroup.ffdhe2048:
43             return DHStandardGroups.rfc7919_ffdhe2048;
44         case NamedGroup.ffdhe3072:
45             return DHStandardGroups.rfc7919_ffdhe3072;
46         case NamedGroup.ffdhe4096:
47             return DHStandardGroups.rfc7919_ffdhe4096;
48         case NamedGroup.ffdhe6144:
49             return DHStandardGroups.rfc7919_ffdhe6144;
50         case NamedGroup.ffdhe8192:
51             return DHStandardGroups.rfc7919_ffdhe8192;
52         default:
53             return null;
54         }
55     }
56 
getMinimumFiniteFieldBits(int cipherSuite)57     public static int getMinimumFiniteFieldBits(int cipherSuite)
58     {
59         /*
60          * NOTE: An equivalent mechanism was added to support a minimum bit-size requirement for ECC
61          * mooted in early drafts of RFC 8442. This requirement was removed in later drafts, so that
62          * mechanism is currently somewhat trivial, and this similarly so.
63          */
64         return isDHCipherSuite(cipherSuite) ? 1 : 0;
65     }
66 
isDHCipherSuite(int cipherSuite)67     public static boolean isDHCipherSuite(int cipherSuite)
68     {
69         switch (TlsUtils.getKeyExchangeAlgorithm(cipherSuite))
70         {
71         case KeyExchangeAlgorithm.DH_anon:
72         case KeyExchangeAlgorithm.DH_DSS:
73         case KeyExchangeAlgorithm.DH_RSA:
74         case KeyExchangeAlgorithm.DHE_DSS:
75         case KeyExchangeAlgorithm.DHE_PSK:
76         case KeyExchangeAlgorithm.DHE_RSA:
77             return true;
78 
79         default:
80             return false;
81         }
82     }
83 
getNamedGroupForDHParameters(BigInteger p, BigInteger g)84     public static int getNamedGroupForDHParameters(BigInteger p, BigInteger g)
85     {
86         int[] namedGroups = new int[]{ NamedGroup.ffdhe2048, NamedGroup.ffdhe3072, NamedGroup.ffdhe4096,
87             NamedGroup.ffdhe6144, NamedGroup.ffdhe8192 };
88 
89         for (int i = 0; i < namedGroups.length; ++i)
90         {
91             int namedGroup = namedGroups[i];
92             DHGroup dhGroup = getNamedDHGroup(namedGroup);
93             if (dhGroup != null && dhGroup.getP().equals(p) && dhGroup.getG().equals(g))
94             {
95                 return namedGroup;
96             }
97         }
98 
99         return -1;
100     }
101 
getStandardGroupForDHParameters(BigInteger p, BigInteger g)102     public static DHGroup getStandardGroupForDHParameters(BigInteger p, BigInteger g)
103     {
104         DHGroup[] standardGroups = new DHGroup[] { DHStandardGroups.rfc7919_ffdhe2048,
105             DHStandardGroups.rfc7919_ffdhe3072, DHStandardGroups.rfc7919_ffdhe4096, DHStandardGroups.rfc7919_ffdhe6144,
106             DHStandardGroups.rfc7919_ffdhe8192, DHStandardGroups.rfc3526_1536, DHStandardGroups.rfc3526_2048,
107             DHStandardGroups.rfc3526_3072, DHStandardGroups.rfc3526_4096, DHStandardGroups.rfc3526_6144,
108             DHStandardGroups.rfc3526_8192, DHStandardGroups.rfc5996_768, DHStandardGroups.rfc5996_1024 };
109 
110         for (int i = 0; i < standardGroups.length; ++i)
111         {
112             DHGroup dhGroup = standardGroups[i];
113             if (dhGroup != null && dhGroup.getP().equals(p) && dhGroup.getG().equals(g))
114             {
115                 return dhGroup;
116             }
117         }
118 
119         return null;
120     }
121 
receiveDHConfig(TlsContext context, TlsDHGroupVerifier dhGroupVerifier, InputStream input)122     public static TlsDHConfig receiveDHConfig(TlsContext context, TlsDHGroupVerifier dhGroupVerifier,
123         InputStream input) throws IOException
124     {
125         BigInteger p = readDHParameter(input);
126         BigInteger g = readDHParameter(input);
127 
128         int namedGroup = getNamedGroupForDHParameters(p, g);
129         if (namedGroup < 0)
130         {
131             DHGroup dhGroup = getStandardGroupForDHParameters(p, g);
132             if (null == dhGroup)
133             {
134                 dhGroup = new DHGroup(p, null, g, 0);
135             }
136 
137             if (!dhGroupVerifier.accept(dhGroup))
138             {
139                 throw new TlsFatalAlert(AlertDescription.insufficient_security);
140             }
141             return new TlsDHConfig(dhGroup);
142         }
143 
144         int[] clientSupportedGroups = context.getSecurityParametersHandshake().getClientSupportedGroups();
145         if (null == clientSupportedGroups || Arrays.contains(clientSupportedGroups, namedGroup))
146         {
147             return new TlsDHConfig(namedGroup, false);
148         }
149 
150         throw new TlsFatalAlert(AlertDescription.illegal_parameter);
151     }
152 
readDHParameter(InputStream input)153     public static BigInteger readDHParameter(InputStream input) throws IOException
154     {
155         return new BigInteger(1, TlsUtils.readOpaque16(input, 1));
156     }
157 
writeDHConfig(TlsDHConfig dhConfig, OutputStream output)158     public static void writeDHConfig(TlsDHConfig dhConfig, OutputStream output)
159         throws IOException
160     {
161         DHGroup group = getDHGroup(dhConfig);
162         writeDHParameter(group.getP(), output);
163         writeDHParameter(group.getG(), output);
164     }
165 
writeDHParameter(BigInteger x, OutputStream output)166     public static void writeDHParameter(BigInteger x, OutputStream output) throws IOException
167     {
168         TlsUtils.writeOpaque16(BigIntegers.asUnsignedByteArray(x), output);
169     }
170 }
171