1 /* Copyright (c) 2018, Google Inc.
2  *
3  * Permission to use, copy, modify, and/or distribute this software for any
4  * purpose with or without fee is hereby granted, provided that the above
5  * copyright notice and this permission notice appear in all copies.
6  *
7  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10  * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
14 
15 #include "./wycheproof_util.h"
16 
17 #include <stdlib.h>
18 
19 #include <algorithm>
20 
21 #include <openssl/bn.h>
22 #include <openssl/digest.h>
23 #include <openssl/ec.h>
24 #include <openssl/nid.h>
25 
26 #include "./file_test.h"
27 
28 
IsValid(const std::vector<std::string> & acceptable_flags) const29 bool WycheproofResult::IsValid(
30     const std::vector<std::string> &acceptable_flags) const {
31   switch (raw_result) {
32     case WycheproofRawResult::kValid:
33       return true;
34     case WycheproofRawResult::kInvalid:
35       return false;
36     case WycheproofRawResult::kAcceptable:
37       for (const auto &flag : flags) {
38         if (std::find(acceptable_flags.begin(), acceptable_flags.end(), flag) ==
39             acceptable_flags.end()) {
40           return false;
41         }
42       }
43       return true;
44   }
45 
46   abort();
47 }
48 
GetWycheproofResult(FileTest * t,WycheproofResult * out)49 bool GetWycheproofResult(FileTest *t, WycheproofResult *out) {
50   std::string result;
51   if (!t->GetAttribute(&result, "result")) {
52     return false;
53   }
54   if (result == "valid") {
55     out->raw_result = WycheproofRawResult::kValid;
56   } else if (result == "invalid") {
57     out->raw_result = WycheproofRawResult::kInvalid;
58   } else if (result == "acceptable") {
59     out->raw_result = WycheproofRawResult::kAcceptable;
60   } else {
61     t->PrintLine("Bad result string '%s'", result.c_str());
62     return false;
63   }
64 
65   out->flags.clear();
66   if (t->HasAttribute("flags")) {
67     std::string flags = t->GetAttributeOrDie("flags");
68     size_t idx = 0;
69     while (idx < flags.size()) {
70       size_t comma = flags.find(',', idx);
71       if (comma == std::string::npos) {
72         comma = flags.size();
73       }
74       out->flags.push_back(flags.substr(idx, comma - idx));
75       idx = comma + 1;
76     }
77   }
78   return true;
79 }
80 
GetWycheproofDigest(FileTest * t,const char * key,bool instruction)81 const EVP_MD *GetWycheproofDigest(FileTest *t, const char *key,
82                                   bool instruction) {
83   std::string name;
84   bool ok =
85       instruction ? t->GetInstruction(&name, key) : t->GetAttribute(&name, key);
86   if (!ok) {
87     return nullptr;
88   }
89   if (name == "SHA-1") {
90     return EVP_sha1();
91   }
92   if (name == "SHA-224") {
93     return EVP_sha224();
94   }
95   if (name == "SHA-256") {
96     return EVP_sha256();
97   }
98   if (name == "SHA-384") {
99     return EVP_sha384();
100   }
101   if (name == "SHA-512") {
102     return EVP_sha512();
103   }
104   t->PrintLine("Unknown digest '%s'", name.c_str());
105   return nullptr;
106 }
107 
GetWycheproofCurve(FileTest * t,const char * key,bool instruction)108 bssl::UniquePtr<EC_GROUP> GetWycheproofCurve(FileTest *t, const char *key,
109                                              bool instruction) {
110   std::string name;
111   bool ok =
112       instruction ? t->GetInstruction(&name, key) : t->GetAttribute(&name, key);
113   if (!ok) {
114     return nullptr;
115   }
116   int nid;
117   if (name == "secp224r1") {
118     nid = NID_secp224r1;
119   } else if (name == "secp256r1") {
120     nid = NID_X9_62_prime256v1;
121   } else if (name == "secp384r1") {
122     nid = NID_secp384r1;
123   } else if (name == "secp521r1") {
124     nid = NID_secp521r1;
125   } else {
126     t->PrintLine("Unknown curve '%s'", name.c_str());
127     return nullptr;
128   }
129   return bssl::UniquePtr<EC_GROUP>(EC_GROUP_new_by_curve_name(nid));
130 }
131 
GetWycheproofBIGNUM(FileTest * t,const char * key,bool instruction)132 bssl::UniquePtr<BIGNUM> GetWycheproofBIGNUM(FileTest *t, const char *key,
133                                             bool instruction) {
134   std::string value;
135   bool ok = instruction ? t->GetInstruction(&value, key)
136                         : t->GetAttribute(&value, key);
137   if (!ok) {
138     return nullptr;
139   }
140   BIGNUM *bn = nullptr;
141   if (BN_hex2bn(&bn, value.c_str()) != static_cast<int>(value.size())) {
142     BN_free(bn);
143     t->PrintLine("Could not decode value '%s'", value.c_str());
144     return nullptr;
145   }
146   bssl::UniquePtr<BIGNUM> ret(bn);
147   if (!value.empty()) {
148     // If the high bit is one, this is a negative number in Wycheproof.
149     // Wycheproof's tests generally mimic Java APIs, including all their
150     // mistakes. See
151     // https://github.com/google/wycheproof/blob/0329f5b751ef102bd6b7b7181b6e049522a887f5/java/com/google/security/wycheproof/JsonUtil.java#L62.
152     if ('0' > value[0] || value[0] > '7') {
153       bssl::UniquePtr<BIGNUM> tmp(BN_new());
154       if (!tmp ||
155           !BN_set_bit(tmp.get(), value.size() * 4) ||
156           !BN_sub(ret.get(), ret.get(), tmp.get())) {
157         return nullptr;
158       }
159     }
160   }
161   return ret;
162 }
163