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