KeyAgreementSpi.java revision 5db505e1f6a68c8d5dfdb0fed0b8607dea7bed96
1package org.bouncycastle.jcajce.provider.asymmetric.ec;
2
3import java.math.BigInteger;
4import java.security.InvalidAlgorithmParameterException;
5import java.security.InvalidKeyException;
6import java.security.Key;
7import java.security.NoSuchAlgorithmException;
8import java.security.PrivateKey;
9import java.security.PublicKey;
10import java.security.SecureRandom;
11import java.security.spec.AlgorithmParameterSpec;
12import java.util.Hashtable;
13
14import javax.crypto.SecretKey;
15import javax.crypto.ShortBufferException;
16import javax.crypto.spec.SecretKeySpec;
17
18import org.bouncycastle.asn1.ASN1ObjectIdentifier;
19import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
20import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
21import org.bouncycastle.asn1.x9.X9IntegerConverter;
22import org.bouncycastle.crypto.BasicAgreement;
23import org.bouncycastle.crypto.CipherParameters;
24import org.bouncycastle.crypto.DerivationFunction;
25import org.bouncycastle.crypto.agreement.ECDHBasicAgreement;
26// BEGIN android-removed
27// import org.bouncycastle.crypto.agreement.ECDHCBasicAgreement;
28// import org.bouncycastle.crypto.agreement.ECMQVBasicAgreement;
29// import org.bouncycastle.crypto.agreement.kdf.DHKDFParameters;
30// import org.bouncycastle.crypto.agreement.kdf.ECDHKEKGenerator;
31// END android-removed
32import org.bouncycastle.crypto.digests.SHA1Digest;
33import org.bouncycastle.crypto.params.ECDomainParameters;
34import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
35import org.bouncycastle.crypto.params.ECPublicKeyParameters;
36// BEGIN android-removed
37// import org.bouncycastle.crypto.params.MQVPrivateParameters;
38// import org.bouncycastle.crypto.params.MQVPublicParameters;
39// END android-removed
40import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
41import org.bouncycastle.jce.interfaces.ECPrivateKey;
42import org.bouncycastle.jce.interfaces.ECPublicKey;
43// BEGIN android-removed
44// import org.bouncycastle.jce.interfaces.MQVPrivateKey;
45// import org.bouncycastle.jce.interfaces.MQVPublicKey;
46// END android-removed
47import org.bouncycastle.util.Integers;
48
49/**
50 * Diffie-Hellman key agreement using elliptic curve keys, ala IEEE P1363
51 * both the simple one, and the simple one with cofactors are supported.
52 *
53 * Also, MQV key agreement per SEC-1
54 */
55public class KeyAgreementSpi
56    extends javax.crypto.KeyAgreementSpi
57{
58    private static final X9IntegerConverter converter = new X9IntegerConverter();
59    private static final Hashtable algorithms = new Hashtable();
60
61    static
62    {
63        Integer i128 = Integers.valueOf(128);
64        Integer i192 = Integers.valueOf(192);
65        Integer i256 = Integers.valueOf(256);
66
67        algorithms.put(NISTObjectIdentifiers.id_aes128_CBC.getId(), i128);
68        algorithms.put(NISTObjectIdentifiers.id_aes192_CBC.getId(), i192);
69        algorithms.put(NISTObjectIdentifiers.id_aes256_CBC.getId(), i256);
70        algorithms.put(NISTObjectIdentifiers.id_aes128_wrap.getId(), i128);
71        algorithms.put(NISTObjectIdentifiers.id_aes192_wrap.getId(), i192);
72        algorithms.put(NISTObjectIdentifiers.id_aes256_wrap.getId(), i256);
73        algorithms.put(PKCSObjectIdentifiers.id_alg_CMS3DESwrap.getId(), i192);
74    }
75
76    private String                 kaAlgorithm;
77    private BigInteger             result;
78    private ECDomainParameters     parameters;
79    private BasicAgreement         agreement;
80    // BEGIN android-removed
81    // private DerivationFunction     kdf;
82    // END android-removed
83
84    private byte[] bigIntToBytes(
85        BigInteger    r)
86    {
87        return converter.integerToBytes(r, converter.getByteLength(parameters.getG().getAffineXCoord()));
88    }
89
90    protected KeyAgreementSpi(
91        String kaAlgorithm,
92        BasicAgreement agreement,
93        DerivationFunction kdf)
94    {
95        this.kaAlgorithm = kaAlgorithm;
96        this.agreement = agreement;
97        // BEGIN android-removed
98        // this.kdf = kdf;
99        // END android-removed
100    }
101
102    protected Key engineDoPhase(
103        Key     key,
104        boolean lastPhase)
105        throws InvalidKeyException, IllegalStateException
106    {
107        if (parameters == null)
108        {
109            throw new IllegalStateException(kaAlgorithm + " not initialised.");
110        }
111
112        if (!lastPhase)
113        {
114            throw new IllegalStateException(kaAlgorithm + " can only be between two parties.");
115        }
116
117        CipherParameters pubKey;
118        // BEGIN android-removed
119        // if (agreement instanceof ECMQVBasicAgreement)
120        // {
121        //     if (!(key instanceof MQVPublicKey))
122        //     {
123        //         throw new InvalidKeyException(kaAlgorithm + " key agreement requires "
124        //             + getSimpleName(MQVPublicKey.class) + " for doPhase");
125        //     }
126        //
127        //     MQVPublicKey mqvPubKey = (MQVPublicKey)key;
128        //     ECPublicKeyParameters staticKey = (ECPublicKeyParameters)
129        //         ECUtil.generatePublicKeyParameter(mqvPubKey.getStaticKey());
130        //     ECPublicKeyParameters ephemKey = (ECPublicKeyParameters)
131        //         ECUtil.generatePublicKeyParameter(mqvPubKey.getEphemeralKey());
132        //
133        //     pubKey = new MQVPublicParameters(staticKey, ephemKey);
134        //
135        //     // TODO Validate that all the keys are using the same parameters?
136        // }
137        // else
138        // END android-removed
139        {
140            if (!(key instanceof PublicKey))
141            {
142                throw new InvalidKeyException(kaAlgorithm + " key agreement requires "
143                    + getSimpleName(ECPublicKey.class) + " for doPhase");
144            }
145
146            pubKey = ECUtil.generatePublicKeyParameter((PublicKey)key);
147
148            // TODO Validate that all the keys are using the same parameters?
149        }
150
151        result = agreement.calculateAgreement(pubKey);
152
153        return null;
154    }
155
156    protected byte[] engineGenerateSecret()
157        throws IllegalStateException
158    {
159        // BEGIN android-removed
160        // if (kdf != null)
161        // {
162        //     throw new UnsupportedOperationException(
163        //         "KDF can only be used when algorithm is known");
164        // }
165        // END android-removed
166
167        return bigIntToBytes(result);
168    }
169
170    protected int engineGenerateSecret(
171        byte[]  sharedSecret,
172        int     offset)
173        throws IllegalStateException, ShortBufferException
174    {
175        byte[] secret = engineGenerateSecret();
176
177        if (sharedSecret.length - offset < secret.length)
178        {
179            throw new ShortBufferException(kaAlgorithm + " key agreement: need " + secret.length + " bytes");
180        }
181
182        System.arraycopy(secret, 0, sharedSecret, offset, secret.length);
183
184        return secret.length;
185    }
186
187    protected SecretKey engineGenerateSecret(
188        String algorithm)
189        throws NoSuchAlgorithmException
190    {
191        byte[] secret = bigIntToBytes(result);
192
193        // BEGIN android-removed
194        // if (kdf != null)
195        // {
196        //     if (!algorithms.containsKey(algorithm))
197        //     {
198        //         throw new NoSuchAlgorithmException("unknown algorithm encountered: " + algorithm);
199        //     }
200        //
201        //     int    keySize = ((Integer)algorithms.get(algorithm)).intValue();
202        //
203        //     DHKDFParameters params = new DHKDFParameters(new ASN1ObjectIdentifier(algorithm), keySize, secret);
204        //
205        //     byte[] keyBytes = new byte[keySize / 8];
206        //     kdf.init(params);
207        //     kdf.generateBytes(keyBytes, 0, keyBytes.length);
208        //     secret = keyBytes;
209        // }
210        // else
211        // END android-removed
212        {
213            // TODO Should we be ensuring the key is the right length?
214        }
215
216        return new SecretKeySpec(secret, algorithm);
217    }
218
219    protected void engineInit(
220        Key                     key,
221        AlgorithmParameterSpec  params,
222        SecureRandom            random)
223        throws InvalidKeyException, InvalidAlgorithmParameterException
224    {
225        // BEGIN android-added
226        if (params != null)
227        {
228            throw new InvalidAlgorithmParameterException("No algorithm parameters supported");
229        }
230        // END android-added
231        initFromKey(key);
232    }
233
234    protected void engineInit(
235        Key             key,
236        SecureRandom    random)
237        throws InvalidKeyException
238    {
239        initFromKey(key);
240    }
241
242    private void initFromKey(Key key)
243        throws InvalidKeyException
244    {
245        // BEGIN android-removed
246        // if (agreement instanceof ECMQVBasicAgreement)
247        // {
248        //     if (!(key instanceof MQVPrivateKey))
249        //     {
250        //         throw new InvalidKeyException(kaAlgorithm + " key agreement requires "
251        //             + getSimpleName(MQVPrivateKey.class) + " for initialisation");
252        //     }
253        //
254        //     MQVPrivateKey mqvPrivKey = (MQVPrivateKey)key;
255        //     ECPrivateKeyParameters staticPrivKey = (ECPrivateKeyParameters)
256        //         ECUtil.generatePrivateKeyParameter(mqvPrivKey.getStaticPrivateKey());
257        //     ECPrivateKeyParameters ephemPrivKey = (ECPrivateKeyParameters)
258        //         ECUtil.generatePrivateKeyParameter(mqvPrivKey.getEphemeralPrivateKey());
259        //
260        //     ECPublicKeyParameters ephemPubKey = null;
261        //     if (mqvPrivKey.getEphemeralPublicKey() != null)
262        //     {
263        //         ephemPubKey = (ECPublicKeyParameters)
264        //             ECUtil.generatePublicKeyParameter(mqvPrivKey.getEphemeralPublicKey());
265        //     }
266        //
267        //     MQVPrivateParameters localParams = new MQVPrivateParameters(staticPrivKey, ephemPrivKey, ephemPubKey);
268        //     this.parameters = staticPrivKey.getParameters();
269        //
270        //     // TODO Validate that all the keys are using the same parameters?
271        //
272        //     agreement.init(localParams);
273        // }
274        // else
275        // END android-removed
276        {
277            if (!(key instanceof PrivateKey))
278            {
279                throw new InvalidKeyException(kaAlgorithm + " key agreement requires "
280                    + getSimpleName(ECPrivateKey.class) + " for initialisation");
281            }
282
283            ECPrivateKeyParameters privKey = (ECPrivateKeyParameters)ECUtil.generatePrivateKeyParameter((PrivateKey)key);
284            this.parameters = privKey.getParameters();
285
286            agreement.init(privKey);
287        }
288    }
289
290    private static String getSimpleName(Class clazz)
291    {
292        String fullName = clazz.getName();
293
294        return fullName.substring(fullName.lastIndexOf('.') + 1);
295    }
296
297    public static class DH
298        extends KeyAgreementSpi
299    {
300        public DH()
301        {
302            super("ECDH", new ECDHBasicAgreement(), null);
303        }
304    }
305
306    // BEGIN android-removed
307    // public static class DHC
308    //     extends KeyAgreementSpi
309    // {
310    //     public DHC()
311    //     {
312    //         super("ECDHC", new ECDHCBasicAgreement(), null);
313    //     }
314    // }
315    //
316    // public static class MQV
317    //     extends KeyAgreementSpi
318    // {
319    //     public MQV()
320    //     {
321    //         super("ECMQV", new ECMQVBasicAgreement(), null);
322    //     }
323    // }
324    //
325    // public static class DHwithSHA1KDF
326    //     extends KeyAgreementSpi
327    // {
328    //     public DHwithSHA1KDF()
329    //     {
330    //         super("ECDHwithSHA1KDF", new ECDHBasicAgreement(), new ECDHKEKGenerator(new SHA1Digest()));
331    //     }
332    // }
333    //
334    // public static class MQVwithSHA1KDF
335    //     extends KeyAgreementSpi
336    // {
337    //     public MQVwithSHA1KDF()
338    //     {
339    //         super("ECMQVwithSHA1KDF", new ECMQVBasicAgreement(), new ECDHKEKGenerator(new SHA1Digest()));
340    //     }
341    // }
342    // END android-removed
343}
344