1package org.bouncycastle.jce.provider;
2
3import java.math.BigInteger;
4import java.security.InvalidAlgorithmParameterException;
5import java.security.InvalidKeyException;
6import java.security.Key;
7import java.security.SecureRandom;
8import java.security.spec.AlgorithmParameterSpec;
9import java.util.Hashtable;
10
11import javax.crypto.KeyAgreementSpi;
12import javax.crypto.SecretKey;
13import javax.crypto.ShortBufferException;
14import javax.crypto.interfaces.DHPrivateKey;
15import javax.crypto.interfaces.DHPublicKey;
16import javax.crypto.spec.DHParameterSpec;
17import javax.crypto.spec.SecretKeySpec;
18
19import org.bouncycastle.crypto.params.DESParameters;
20import org.bouncycastle.util.Strings;
21
22/**
23 * Diffie-Hellman key agreement. There's actually a better way of doing this
24 * if you are using long term public keys, see the light-weight version for
25 * details.
26 */
27public class JCEDHKeyAgreement
28    extends KeyAgreementSpi
29{
30    private BigInteger      x;
31    private BigInteger      p;
32    private BigInteger      g;
33    private BigInteger      result;
34
35    private static final Hashtable algorithms = new Hashtable();
36
37    static
38    {
39        // BEGIN android-changed
40        Integer i64 = Integer.valueOf(64);
41        Integer i192 = Integer.valueOf(192);
42        Integer i128 = Integer.valueOf(128);
43        Integer i256 = Integer.valueOf(256);
44        // END android-changed
45
46        algorithms.put("DES", i64);
47        algorithms.put("DESEDE", i192);
48        algorithms.put("BLOWFISH", i128);
49        algorithms.put("AES", i256);
50    }
51
52    private byte[] bigIntToBytes(
53        BigInteger    r)
54    {
55        byte[]    tmp = r.toByteArray();
56
57        if (tmp[0] == 0)
58        {
59            byte[]    ntmp = new byte[tmp.length - 1];
60
61            System.arraycopy(tmp, 1, ntmp, 0, ntmp.length);
62            return ntmp;
63        }
64
65        return tmp;
66    }
67
68    protected Key engineDoPhase(
69        Key     key,
70        boolean lastPhase)
71        throws InvalidKeyException, IllegalStateException
72    {
73        if (x == null)
74        {
75            throw new IllegalStateException("Diffie-Hellman not initialised.");
76        }
77
78        if (!(key instanceof DHPublicKey))
79        {
80            throw new InvalidKeyException("DHKeyAgreement doPhase requires DHPublicKey");
81        }
82        DHPublicKey pubKey = (DHPublicKey)key;
83
84        if (!pubKey.getParams().getG().equals(g) || !pubKey.getParams().getP().equals(p))
85        {
86            throw new InvalidKeyException("DHPublicKey not for this KeyAgreement!");
87        }
88
89        if (lastPhase)
90        {
91            result = ((DHPublicKey)key).getY().modPow(x, p);
92            return null;
93        }
94        else
95        {
96            result = ((DHPublicKey)key).getY().modPow(x, p);
97        }
98
99        return new JCEDHPublicKey(result, pubKey.getParams());
100    }
101
102    protected byte[] engineGenerateSecret()
103        throws IllegalStateException
104    {
105        if (x == null)
106        {
107            throw new IllegalStateException("Diffie-Hellman not initialised.");
108        }
109
110        return bigIntToBytes(result);
111    }
112
113    protected int engineGenerateSecret(
114        byte[]  sharedSecret,
115        int     offset)
116        throws IllegalStateException, ShortBufferException
117    {
118        if (x == null)
119        {
120            throw new IllegalStateException("Diffie-Hellman not initialised.");
121        }
122
123        byte[]  secret = bigIntToBytes(result);
124
125        if (sharedSecret.length - offset < secret.length)
126        {
127            throw new ShortBufferException("DHKeyAgreement - buffer too short");
128        }
129
130        System.arraycopy(secret, 0, sharedSecret, offset, secret.length);
131
132        return secret.length;
133    }
134
135    protected SecretKey engineGenerateSecret(
136        String algorithm)
137    {
138        if (x == null)
139        {
140            throw new IllegalStateException("Diffie-Hellman not initialised.");
141        }
142
143        String algKey = Strings.toUpperCase(algorithm);
144        byte[] res = bigIntToBytes(result);
145
146        if (algorithms.containsKey(algKey))
147        {
148            Integer length = (Integer)algorithms.get(algKey);
149
150            byte[] key = new byte[length.intValue() / 8];
151            System.arraycopy(res, 0, key, 0, key.length);
152
153            if (algKey.startsWith("DES"))
154            {
155                DESParameters.setOddParity(key);
156            }
157
158            return new SecretKeySpec(key, algorithm);
159        }
160
161        return new SecretKeySpec(res, algorithm);
162    }
163
164    protected void engineInit(
165        Key                     key,
166        AlgorithmParameterSpec  params,
167        SecureRandom            random)
168        throws InvalidKeyException, InvalidAlgorithmParameterException
169    {
170        if (!(key instanceof DHPrivateKey))
171        {
172            throw new InvalidKeyException("DHKeyAgreement requires DHPrivateKey for initialisation");
173        }
174        DHPrivateKey    privKey = (DHPrivateKey)key;
175
176        if (params != null)
177        {
178            if (!(params instanceof DHParameterSpec))
179            {
180                throw new InvalidAlgorithmParameterException("DHKeyAgreement only accepts DHParameterSpec");
181            }
182            DHParameterSpec p = (DHParameterSpec)params;
183
184            this.p = p.getP();
185            this.g = p.getG();
186        }
187        else
188        {
189            this.p = privKey.getParams().getP();
190            this.g = privKey.getParams().getG();
191        }
192
193        this.x = this.result = privKey.getX();
194    }
195
196    protected void engineInit(
197        Key             key,
198        SecureRandom    random)
199        throws InvalidKeyException
200    {
201        if (!(key instanceof DHPrivateKey))
202        {
203            throw new InvalidKeyException("DHKeyAgreement requires DHPrivateKey");
204        }
205
206        DHPrivateKey    privKey = (DHPrivateKey)key;
207
208        this.p = privKey.getParams().getP();
209        this.g = privKey.getParams().getG();
210        this.x = this.result = privKey.getX();
211    }
212}
213