1 /**
2 * ECDSA
3 * 
4 * Copyright:
5 * (C) 2007 Falko Strenzke, FlexSecure GmbH
6 *          Manuel Hartl, FlexSecure GmbH
7 * (C) 2008-2010 Jack Lloyd
8 * (C) 2014-2015 Etienne Cimon
9 *
10 * License:
11 * Botan is released under the Simplified BSD License (see LICENSE.md)
12 */
13 module botan.pubkey.algo.ecc_key;
14 
15 import botan.constants;
16 static if (BOTAN_HAS_PUBLIC_KEY_CRYPTO && (BOTAN_HAS_ECDH || BOTAN_HAS_ECDSA || BOTAN_HAS_GOST_34_10_2001)):
17 
18 public import botan.pubkey.pubkey;
19 public import botan.pubkey.algo.ec_group;
20 public import botan.math.numbertheory.numthry;
21 public import botan.math.ec_gfp.curve_gfp;
22 public import botan.math.ec_gfp.point_gfp;
23 public import botan.pubkey.pk_keys;
24 public import botan.pubkey.x509_key;
25 import botan.rng.rng;
26 import botan.pubkey.pkcs8;
27 import botan.asn1.der_enc;
28 import botan.asn1.ber_dec;
29 import memutils.vector;
30 import botan.utils.mem_ops;
31 import botan.utils.exceptn;
32 
33 /**
34 * This class represents abstract ECC public keys. When encoding a key
35 * via an encoder that can be accessed via the corresponding member
36 * functions, the key will decide upon its internally stored encoding
37 * information whether to encode itself with or without domain
38 * parameters, or using the domain parameter oid. Furthermore, a public
39 * key without domain parameters can be decoded. In that case, it
40 * cannot be used for verification until its domain parameters are set
41 * by calling the corresponding member function.
42 */
43 class ECPublicKey : PublicKey
44 {
45 public:
46     this(T)(in T options, const ref ECGroup dom_par, const ref PointGFp pub_point) 
47     {
48         decodeOptions(options);
49 
50         m_domain_params = dom_par.dup;
51         m_public_key = pub_point.dup;
52         m_domain_encoding = EC_DOMPAR_ENC_EXPLICIT;
53 
54         if (domain().getCurve() != publicPoint().getCurve())
55             throw new InvalidArgument("ECPublicKey: curve mismatch in constructor");
56     }
57 
58     this(T)(in T options, in AlgorithmIdentifier alg_id, 
59         const ref SecureVector!ubyte key_bits) 
60     {
61         decodeOptions(options);
62 
63         m_domain_params = ECGroup(alg_id.parameters);
64         m_domain_encoding = EC_DOMPAR_ENC_EXPLICIT;
65         m_public_key = OS2ECP(key_bits, domain().getCurve());
66     }
67 
68     protected this(T)(in T options, in AlgorithmIdentifier alg_id) 
69     {
70         decodeOptions(options);
71         //logTrace("ECGroup with alg_id.parameters");
72         m_domain_params = ECGroup(alg_id.parameters);
73         m_domain_encoding = EC_DOMPAR_ENC_EXPLICIT;
74     }
75 
76     final void decodeOptions(T)(in T options) {
77         static if (__traits(hasMember, T, "checkKey"))
78             m_check_key = &options.checkKey;
79         static if (__traits(hasMember, T, "algorithmIdentifier"))
80             m_algorithm_identifier = &options.algorithmIdentifier;
81         static if (__traits(hasMember, T, "x509SubjectPublicKey"))
82             m_subject_public_key = &options.x509SubjectPublicKey;
83         static if (__traits(hasMember, T, "msgParts"))
84             m_msg_parts = options.msgParts;
85         static if (__traits(hasMember, T, "algoName"))
86             m_algo_name = options.algoName;
87         else static assert(false, "No algoName found in " ~ T.stringof);
88     }
89 
90     /// Used for object casting to the right type in the factory.
91     final override @property string algoName() const {
92         return m_algo_name;
93     }
94 
95     /**
96     * Get the public point of this key.
97     * Throws: $(D InvalidState) is thrown if the
98     * domain parameters of this point are not set
99     * Returns: the public point of this key
100     */
101     final ref const(PointGFp) publicPoint() const { return m_public_key; }
102 
103     final override size_t maxInputBits() const { return domain().getOrder().bits(); }
104 
105     final override size_t messagePartSize() const { 
106         if (m_msg_parts == 1) return 0;
107 
108         return domain().getOrder().bytes(); 
109     }
110 
111     final override size_t messageParts() const { return m_msg_parts; }
112 
113     final override AlgorithmIdentifier algorithmIdentifier() const
114     {
115         if (m_algorithm_identifier)
116             return m_algorithm_identifier(this);
117         return AlgorithmIdentifier(getOid(), DER_domain());
118     }
119 
120     final override Vector!ubyte x509SubjectPublicKey() const
121     {
122         if (m_subject_public_key)
123             return m_subject_public_key(this);
124         return unlock(EC2OSP(publicPoint(), PointGFp.COMPRESSED));
125     }
126 
127     override bool checkKey(RandomNumberGenerator rng, bool b) const
128     {
129         return publicPoint().onTheCurve();
130     }
131 
132     /**
133     * Get the domain parameters of this key.
134     * Throws: $(D InvalidState) is thrown if the domain parameters of this point are not set
135     * Returns: the domain parameters of this key
136     */
137     final ref const(ECGroup) domain() const { return m_domain_params; }
138 
139     /**
140     * Set the domain parameter encoding to be used when encoding this key.
141     *
142     * Params:
143     *  form = the encoding to use
144     */
145     final void setParameterEncoding(ECGroupEncoding form)
146     {
147         if (form != EC_DOMPAR_ENC_EXPLICIT && form != EC_DOMPAR_ENC_IMPLICITCA && form != EC_DOMPAR_ENC_OID)
148             throw new InvalidArgument("Invalid encoding form for EC-key object specified");
149         
150         if ((form == EC_DOMPAR_ENC_OID) && (m_domain_params.getOid() == ""))
151             throw new InvalidArgument("Invalid encoding form OID specified for "
152                                        ~ "EC-key object whose corresponding domain "
153                                        ~ "parameters are without oid");
154         
155         m_domain_encoding = form;
156     }
157 
158     /**
159     * Return the DER encoding of this keys domain in whatever format
160     * is preset for this particular key
161     */
162     Vector!ubyte DER_domain() const { return domain().DER_encode(domainFormat()); }
163 
164     /**
165     * Get the domain parameter encoding to be used when encoding this key.
166     * Returns: the encoding to use
167     */
168     ECGroupEncoding domainFormat() const { return m_domain_encoding; }
169 
170     override size_t estimatedStrength() const
171     {
172         return domain().getCurve().getP().bits() / 2;
173     }
174 
175     /**
176     * Returns: public point value
177     */
178     Vector!ubyte publicValue() const
179     { return unlock(EC2OSP(publicPoint(), PointGFp.UNCOMPRESSED)); }
180 protected:
181 
182     ECGroup m_domain_params;
183     PointGFp m_public_key;
184     ECGroupEncoding m_domain_encoding;
185 
186     string m_algo_name;
187     short m_msg_parts = 1;
188     bool function(in ECPrivateKey, RandomNumberGenerator, bool) m_check_key;
189     Vector!ubyte function(in ECPublicKey) m_subject_public_key;
190     AlgorithmIdentifier function(in ECPublicKey) m_algorithm_identifier;
191 }
192 
193 /**
194 * This abstract class represents ECC private keys
195 */
196 final class ECPrivateKey : ECPublicKey, PrivateKey, PKKeyAgreementKey
197 {
198 public:
199     /**
200     * ECPrivateKey constructor
201     */
202     this(T)(in T options, RandomNumberGenerator rng, const ref ECGroup ec_group, const ref BigInt private_key) 
203     {
204         if (private_key == 0) {
205             auto bi = BigInt(1);
206             m_private_key = BigInt.randomInteger(rng, bi, ec_group.getOrder());
207         }
208         else
209             m_private_key = private_key.dup;
210 
211         PointGFp public_key = ec_group.getBasePoint() * &m_private_key;
212         
213         assert(public_key.onTheCurve(), "Generated public key point was on the curve");
214 
215         // logTrace("private key: ", m_private_key.toString());
216         super(options, ec_group, public_key);
217     }
218 
219     this(T)(in T options, const ref AlgorithmIdentifier alg_id, const ref SecureVector!ubyte key_bits) 
220     {
221         super(options, alg_id);
222         PointGFp public_key;
223         OID key_parameters = OID();
224 
225         SecureVector!ubyte public_key_bits;
226         
227         BERDecoder(key_bits)
228                 .startCons(ASN1Tag.SEQUENCE)
229                 .decodeAndCheck!size_t(1, "Unknown version code for ECC key")
230                 .decodeOctetStringBigint(m_private_key)
231                 .decodeOptional(key_parameters, (cast(ASN1Tag) 0), ASN1Tag.PRIVATE, key_parameters)
232                 .decodeOptionalString(public_key_bits, ASN1Tag.BIT_STRING, 1, ASN1Tag.PRIVATE)
233                 .endCons();
234         if (!key_parameters.empty && key_parameters != alg_id.oid)
235             throw new DecodingError("ECPrivateKey - inner and outer OIDs did not match");
236 
237         if (public_key_bits.empty)
238         {
239             m_public_key = domain().getBasePoint() * &m_private_key;
240             assert(m_public_key.onTheCurve(), "Public point derived from loaded key was on the curve");
241         }
242         else
243         {
244             m_public_key = OS2ECP(public_key_bits, m_domain_params.getCurve());
245             // OS2ECP verifies that the point is on the curve
246         }
247     }
248 
249     override bool checkKey(RandomNumberGenerator rng, bool b) const
250     {
251         if (m_check_key)
252             return m_check_key(this, rng, b);
253         
254         return super.checkKey(rng, b);
255     }
256 
257     SecureVector!ubyte pkcs8PrivateKey() const
258     {
259         return DEREncoder()
260                 .startCons(ASN1Tag.SEQUENCE)
261                 .encode(cast(size_t)(1))
262                 .encode(BigInt.encode1363(m_private_key, m_private_key.bytes()),
263                         ASN1Tag.OCTET_STRING)
264                 .endCons()
265                 .getContents();
266     }
267 
268     override AlgorithmIdentifier pkcs8AlgorithmIdentifier() const { 
269         if (algoName() == "GOST-34.10")
270             return AlgorithmIdentifier(getOid(), DER_domain());
271         return super.algorithmIdentifier();
272     }
273 
274     /**
275     * Get the private key value of this key object.
276     * Returns: the private key value of this key object
277     */
278     ref const(BigInt) privateValue() const
279     {
280         if (m_private_key == 0)
281             throw new InvalidState("ECPrivateKey.private_value - uninitialized");
282         
283         return m_private_key;
284     }
285 
286     override Vector!ubyte publicValue() const { return super.publicValue(); }
287 
288 private:
289     BigInt m_private_key;
290 }