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;
9
10import javax.crypto.KeyAgreementSpi;
11import javax.crypto.SecretKey;
12import javax.crypto.ShortBufferException;
13import javax.crypto.interfaces.DHPrivateKey;
14import javax.crypto.interfaces.DHPublicKey;
15import javax.crypto.spec.DHParameterSpec;
16import javax.crypto.spec.SecretKeySpec;
17
18/**
19 * Diffie-Hellman key agreement. There's actually a better way of doing this
20 * if you are using long term public keys, see the light-weight version for
21 * details.
22 */
23public class JCEDHKeyAgreement
24    extends KeyAgreementSpi
25{
26    private BigInteger      x;
27    private BigInteger      p;
28    private BigInteger      g;
29    private BigInteger      result;
30
31    private SecureRandom    random;
32
33    private byte[] bigIntToBytes(
34        BigInteger    r)
35    {
36        byte[]    tmp = r.toByteArray();
37
38        if (tmp[0] == 0)
39        {
40            byte[]    ntmp = new byte[tmp.length - 1];
41
42            System.arraycopy(tmp, 1, ntmp, 0, ntmp.length);
43            return ntmp;
44        }
45
46        return tmp;
47    }
48
49    protected Key engineDoPhase(
50        Key     key,
51        boolean lastPhase)
52        throws InvalidKeyException, IllegalStateException
53    {
54        if (x == null)
55        {
56            throw new IllegalStateException("Diffie-Hellman not initialised.");
57        }
58
59        if (!(key instanceof DHPublicKey))
60        {
61            throw new InvalidKeyException("DHKeyAgreement doPhase requires DHPublicKey");
62        }
63        DHPublicKey pubKey = (DHPublicKey)key;
64
65        if (!pubKey.getParams().getG().equals(g) || !pubKey.getParams().getP().equals(p))
66        {
67            throw new InvalidKeyException("DHPublicKey not for this KeyAgreement!");
68        }
69
70        if (lastPhase)
71        {
72            result = ((DHPublicKey)key).getY().modPow(x, p);
73            return null;
74        }
75        else
76        {
77            result = ((DHPublicKey)key).getY().modPow(x, p);
78        }
79
80        return new JCEDHPublicKey(result, pubKey.getParams());
81    }
82
83    protected byte[] engineGenerateSecret()
84        throws IllegalStateException
85    {
86        if (x == null)
87        {
88            throw new IllegalStateException("Diffie-Hellman not initialised.");
89        }
90
91        return bigIntToBytes(result);
92    }
93
94    protected int engineGenerateSecret(
95        byte[]  sharedSecret,
96        int     offset)
97        throws IllegalStateException, ShortBufferException
98    {
99        if (x == null)
100        {
101            throw new IllegalStateException("Diffie-Hellman not initialised.");
102        }
103
104        byte[]  secret = bigIntToBytes(result);
105
106        if (sharedSecret.length - offset < secret.length)
107        {
108            throw new ShortBufferException("DHKeyAgreement - buffer too short");
109        }
110
111        System.arraycopy(secret, 0, sharedSecret, offset, secret.length);
112
113        return secret.length;
114    }
115
116    protected SecretKey engineGenerateSecret(
117        String algorithm)
118    {
119        if (x == null)
120        {
121            throw new IllegalStateException("Diffie-Hellman not initialised.");
122        }
123
124        return new SecretKeySpec(bigIntToBytes(result), algorithm);
125    }
126
127    protected void engineInit(
128        Key                     key,
129        AlgorithmParameterSpec  params,
130        SecureRandom            random)
131        throws InvalidKeyException, InvalidAlgorithmParameterException
132    {
133        if (!(key instanceof DHPrivateKey))
134        {
135            throw new InvalidKeyException("DHKeyAgreement requires DHPrivateKey for initialisation");
136        }
137        DHPrivateKey    privKey = (DHPrivateKey)key;
138
139        this.random = random;
140
141        if (params != null)
142        {
143            if (!(params instanceof DHParameterSpec))
144            {
145                throw new InvalidAlgorithmParameterException("DHKeyAgreement only accepts DHParameterSpec");
146            }
147            DHParameterSpec p = (DHParameterSpec)params;
148
149            this.p = p.getP();
150            this.g = p.getG();
151        }
152        else
153        {
154            this.p = privKey.getParams().getP();
155            this.g = privKey.getParams().getG();
156        }
157
158        this.x = this.result = privKey.getX();
159    }
160
161    protected void engineInit(
162        Key             key,
163        SecureRandom    random)
164        throws InvalidKeyException
165    {
166        if (!(key instanceof DHPrivateKey))
167        {
168            throw new InvalidKeyException("DHKeyAgreement requires DHPrivateKey");
169        }
170
171        DHPrivateKey    privKey = (DHPrivateKey)key;
172
173        this.random = random;
174        this.p = privKey.getParams().getP();
175        this.g = privKey.getParams().getG();
176        this.x = this.result = privKey.getX();
177    }
178}
179