1package org.bouncycastle.crypto.agreement;
2
3import java.math.BigInteger;
4
5import org.bouncycastle.crypto.BasicAgreement;
6import org.bouncycastle.crypto.CipherParameters;
7import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
8import org.bouncycastle.crypto.params.ECPublicKeyParameters;
9// BEGIN android-added
10import org.bouncycastle.math.ec.ECCurve;
11// END android-added
12import org.bouncycastle.math.ec.ECPoint;
13
14/**
15 * P1363 7.2.1 ECSVDP-DH
16 *
17 * ECSVDP-DH is Elliptic Curve Secret Value Derivation Primitive,
18 * Diffie-Hellman version. It is based on the work of [DH76], [Mil86],
19 * and [Kob87]. This primitive derives a shared secret value from one
20 * party's private key and another party's public key, where both have
21 * the same set of EC domain parameters. If two parties correctly
22 * execute this primitive, they will produce the same output. This
23 * primitive can be invoked by a scheme to derive a shared secret key;
24 * specifically, it may be used with the schemes ECKAS-DH1 and
25 * DL/ECKAS-DH2. It assumes that the input keys are valid (see also
26 * Section 7.2.2).
27 */
28public class ECDHBasicAgreement
29    implements BasicAgreement
30{
31    private ECPrivateKeyParameters key;
32
33    public void init(
34        CipherParameters key)
35    {
36        this.key = (ECPrivateKeyParameters)key;
37    }
38
39    public int getFieldSize()
40    {
41        return (key.getParameters().getCurve().getFieldSize() + 7) / 8;
42    }
43
44    public BigInteger calculateAgreement(
45        CipherParameters pubKey)
46    {
47        // BEGIN android-changed
48        ECPoint peerPoint = ((ECPublicKeyParameters) pubKey).getQ();
49        ECCurve myCurve = key.getParameters().getCurve();
50        if (peerPoint.isInfinity()) {
51          throw new IllegalStateException("Infinity is not a valid public key for ECDH");
52        }
53        try {
54          myCurve.validatePoint(peerPoint.getXCoord().toBigInteger(),
55              peerPoint.getYCoord().toBigInteger());
56        } catch (IllegalArgumentException ex) {
57          throw new IllegalStateException("The peer public key must be on the curve for ECDH");
58        }
59        // Explicitly construct a public key using the private key's curve.
60        ECPoint pubPoint = myCurve.createPoint(peerPoint.getXCoord().toBigInteger(),
61            peerPoint.getYCoord().toBigInteger());
62        ECPublicKeyParameters pub = (ECPublicKeyParameters)pubKey;
63        if (!pub.getParameters().equals(key.getParameters()))
64        {
65            throw new IllegalStateException("ECDH public key has wrong domain parameters");
66        }
67        ECPoint P = pubPoint.multiply(key.getD()).normalize();
68        // END android-changed
69
70        if (P.isInfinity())
71        {
72            throw new IllegalStateException("Infinity is not a valid agreement value for ECDH");
73        }
74
75        return P.getAffineXCoord().toBigInteger();
76    }
77}
78