1 /*
2  * Copyright (c) 2006, 2007, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package sun.security.pkcs11;
27 
28 import java.security.*;
29 import java.security.interfaces.ECPublicKey;
30 import java.security.spec.AlgorithmParameterSpec;
31 
32 import javax.crypto.*;
33 
34 import static sun.security.pkcs11.TemplateManager.*;
35 import sun.security.pkcs11.wrapper.*;
36 import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
37 
38 /**
39  * KeyAgreement implementation for ECDH.
40  *
41  * @author  Andreas Sterbenz
42  * @since   1.6
43  */
44 final class P11ECDHKeyAgreement extends KeyAgreementSpi {
45 
46     // token instance
47     private final Token token;
48 
49     // algorithm name
50     private final String algorithm;
51 
52     // mechanism id
53     private final long mechanism;
54 
55     // private key, if initialized
56     private P11Key privateKey;
57 
58     // encoded public point, non-null between doPhase() and generateSecret() only
59     private byte[] publicValue;
60 
61     // length of the secret to be derived
62     private int secretLen;
63 
P11ECDHKeyAgreement(Token token, String algorithm, long mechanism)64     P11ECDHKeyAgreement(Token token, String algorithm, long mechanism) {
65         super();
66         this.token = token;
67         this.algorithm = algorithm;
68         this.mechanism = mechanism;
69     }
70 
71     // see JCE spec
engineInit(Key key, SecureRandom random)72     protected void engineInit(Key key, SecureRandom random)
73             throws InvalidKeyException {
74         if (key instanceof PrivateKey == false) {
75             throw new InvalidKeyException
76                         ("Key must be instance of PrivateKey");
77         }
78         privateKey = P11KeyFactory.convertKey(token, key, "EC");
79         publicValue = null;
80     }
81 
82     // see JCE spec
engineInit(Key key, AlgorithmParameterSpec params, SecureRandom random)83     protected void engineInit(Key key, AlgorithmParameterSpec params,
84             SecureRandom random) throws InvalidKeyException,
85             InvalidAlgorithmParameterException {
86         if (params != null) {
87             throw new InvalidAlgorithmParameterException
88                         ("Parameters not supported");
89         }
90         engineInit(key, random);
91     }
92 
93     // see JCE spec
engineDoPhase(Key key, boolean lastPhase)94     protected Key engineDoPhase(Key key, boolean lastPhase)
95             throws InvalidKeyException, IllegalStateException {
96         if (privateKey == null) {
97             throw new IllegalStateException("Not initialized");
98         }
99         if (publicValue != null) {
100             throw new IllegalStateException("Phase already executed");
101         }
102         if (lastPhase == false) {
103             throw new IllegalStateException
104                 ("Only two party agreement supported, lastPhase must be true");
105         }
106         if (key instanceof ECPublicKey == false) {
107             throw new InvalidKeyException
108                 ("Key must be a PublicKey with algorithm EC");
109         }
110         ECPublicKey ecKey = (ECPublicKey)key;
111         int keyLenBits = ecKey.getParams().getCurve().getField().getFieldSize();
112         secretLen = (keyLenBits + 7) >> 3;
113         publicValue = P11ECKeyFactory.getEncodedPublicValue(ecKey);
114         return null;
115     }
116 
117     // see JCE spec
engineGenerateSecret()118     protected byte[] engineGenerateSecret() throws IllegalStateException {
119         if ((privateKey == null) || (publicValue == null)) {
120             throw new IllegalStateException("Not initialized correctly");
121         }
122         Session session = null;
123         try {
124             session = token.getOpSession();
125             CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
126                 new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY),
127                 new CK_ATTRIBUTE(CKA_KEY_TYPE, CKK_GENERIC_SECRET),
128             };
129             CK_ECDH1_DERIVE_PARAMS ckParams =
130                     new CK_ECDH1_DERIVE_PARAMS(CKD_NULL, null, publicValue);
131             attributes = token.getAttributes
132                 (O_GENERATE, CKO_SECRET_KEY, CKK_GENERIC_SECRET, attributes);
133             long keyID = token.p11.C_DeriveKey(session.id(),
134                 new CK_MECHANISM(mechanism, ckParams), privateKey.keyID,
135                 attributes);
136             attributes = new CK_ATTRIBUTE[] {
137                 new CK_ATTRIBUTE(CKA_VALUE)
138             };
139             token.p11.C_GetAttributeValue(session.id(), keyID, attributes);
140             byte[] secret = attributes[0].getByteArray();
141             token.p11.C_DestroyObject(session.id(), keyID);
142             return secret;
143         } catch (PKCS11Exception e) {
144             throw new ProviderException("Could not derive key", e);
145         } finally {
146             publicValue = null;
147             token.releaseSession(session);
148         }
149     }
150 
151     // see JCE spec
engineGenerateSecret(byte[] sharedSecret, int offset)152     protected int engineGenerateSecret(byte[] sharedSecret, int
153             offset) throws IllegalStateException, ShortBufferException {
154         if (offset + secretLen > sharedSecret.length) {
155             throw new ShortBufferException("Need " + secretLen
156                 + " bytes, only " + (sharedSecret.length - offset) + " available");
157         }
158         byte[] secret = engineGenerateSecret();
159         System.arraycopy(secret, 0, sharedSecret, offset, secret.length);
160         return secret.length;
161     }
162 
163     // see JCE spec
engineGenerateSecret(String algorithm)164     protected SecretKey engineGenerateSecret(String algorithm)
165             throws IllegalStateException, NoSuchAlgorithmException,
166             InvalidKeyException {
167         if (algorithm == null) {
168             throw new NoSuchAlgorithmException("Algorithm must not be null");
169         }
170         if (algorithm.equals("TlsPremasterSecret") == false) {
171             throw new NoSuchAlgorithmException
172                 ("Only supported for algorithm TlsPremasterSecret");
173         }
174         return nativeGenerateSecret(algorithm);
175     }
176 
nativeGenerateSecret(String algorithm)177     private SecretKey nativeGenerateSecret(String algorithm)
178             throws IllegalStateException, NoSuchAlgorithmException,
179             InvalidKeyException {
180         if ((privateKey == null) || (publicValue == null)) {
181             throw new IllegalStateException("Not initialized correctly");
182         }
183         long keyType = CKK_GENERIC_SECRET;
184         Session session = null;
185         try {
186             session = token.getObjSession();
187             CK_ATTRIBUTE[] attributes = new CK_ATTRIBUTE[] {
188                 new CK_ATTRIBUTE(CKA_CLASS, CKO_SECRET_KEY),
189                 new CK_ATTRIBUTE(CKA_KEY_TYPE, keyType),
190             };
191             CK_ECDH1_DERIVE_PARAMS ckParams =
192                     new CK_ECDH1_DERIVE_PARAMS(CKD_NULL, null, publicValue);
193             attributes = token.getAttributes
194                 (O_GENERATE, CKO_SECRET_KEY, keyType, attributes);
195             long keyID = token.p11.C_DeriveKey(session.id(),
196                 new CK_MECHANISM(mechanism, ckParams), privateKey.keyID,
197                 attributes);
198             CK_ATTRIBUTE[] lenAttributes = new CK_ATTRIBUTE[] {
199                 new CK_ATTRIBUTE(CKA_VALUE_LEN),
200             };
201             token.p11.C_GetAttributeValue(session.id(), keyID, lenAttributes);
202             int keyLen = (int)lenAttributes[0].getLong();
203             SecretKey key = P11Key.secretKey
204                         (session, keyID, algorithm, keyLen << 3, attributes);
205             return key;
206         } catch (PKCS11Exception e) {
207             throw new InvalidKeyException("Could not derive key", e);
208         } finally {
209             publicValue = null;
210             token.releaseSession(session);
211         }
212     }
213 
214 }
215