| // Copyright (c) 2012 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef CHAPS_CHAPS_UTILITY_H_ |
| #define CHAPS_CHAPS_UTILITY_H_ |
| |
| #include <string.h> |
| |
| #include <iterator> |
| #include <sstream> |
| #include <string> |
| #include <vector> |
| |
| #include <base/check.h> |
| #include <base/logging.h> |
| #include <brillo/secure_blob.h> |
| #include <crypto/scoped_openssl_types.h> |
| |
| #include "chaps/chaps.h" |
| #include "pkcs11/cryptoki.h" |
| |
| namespace chaps { |
| |
| enum class DigestAlgorithm { |
| MD5 = 0, |
| SHA1 = 1, |
| SHA256 = 2, |
| SHA384 = 3, |
| SHA512 = 4, |
| // Should be put the last if it is not in kDigestAlgorithmEncoding |
| NoDigest |
| }; |
| |
| enum class RsaPaddingScheme { |
| UNKNOWN_PADDING_SCHEME = 0, |
| RSASSA_PKCS1_V1_5, |
| RSASSA_PSS, |
| }; |
| |
| // These strings are the DER encodings of the DigestInfo values for the |
| // supported digest algorithms. See PKCS #1 v2.1: 9.2. |
| const struct { |
| const char* encoding; |
| size_t encoding_len; |
| } kDigestAlgorithmEncoding[] = { |
| {/* MD5 = 0 */ |
| "\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x05\x05\x00\x04" |
| "\x10", |
| 18}, |
| {/* SHA1 = 1 */ |
| "\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14", 15}, |
| {/* SHA256 = 2 */ |
| "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04" |
| "\x20", |
| 19}, |
| {/* SHA384 = 3 */ |
| "\x30\x41\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02\x05\x00\x04" |
| "\x30", |
| 19}, |
| {/* SHA512 = 4 */ |
| "\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x05\x00\x04" |
| "\x40", |
| 19}, |
| }; |
| |
| // Get the algorithm ID for DigestInfo structure |
| inline std::string GetDigestAlgorithmEncoding(DigestAlgorithm alg) { |
| size_t alg_index = static_cast<size_t>(alg); |
| if (alg_index >= std::size(kDigestAlgorithmEncoding)) { |
| return std::string(); |
| } |
| return std::string(kDigestAlgorithmEncoding[alg_index].encoding, |
| kDigestAlgorithmEncoding[alg_index].encoding_len); |
| } |
| |
| // Copy*ToCharBuffer copies to a space-padded CK_UTF8CHAR buffer (not |
| // NULL-terminated). |
| inline void CopyStringToCharBuffer(const std::string& source, |
| CK_UTF8CHAR_PTR buffer, |
| size_t buffer_size) { |
| size_t copy_size = source.length(); |
| if (copy_size > buffer_size) |
| copy_size = buffer_size; |
| memset(buffer, ' ', buffer_size); |
| if (copy_size > 0) |
| memcpy(buffer, source.data(), copy_size); |
| } |
| |
| inline void CopyVectorToCharBuffer(const std::vector<uint8_t>& source, |
| CK_UTF8CHAR_PTR buffer, |
| size_t buffer_size) { |
| size_t copy_size = source.size(); |
| if (copy_size > buffer_size) |
| copy_size = buffer_size; |
| memset(buffer, ' ', buffer_size); |
| if (copy_size > 0) |
| memcpy(buffer, source.data(), copy_size); |
| } |
| |
| // RVToString stringifies a PKCS #11 return value. E.g. CKR_OK --> "CKR_OK". |
| EXPORT_SPEC const char* CK_RVToString(CK_RV value); |
| |
| // AttributeToString stringifies a PKCS #11 attribute type. |
| std::string AttributeToString(CK_ATTRIBUTE_TYPE attribute); |
| |
| // StringToAttribute tries to parse the input string |attribute_string| into an |
| // attribute |output|. It'll return true iff the |attribute_string| is parsed |
| // into attribute. |
| EXPORT_SPEC bool StringToAttribute(std::string attribute_string, |
| CK_ATTRIBUTE_TYPE* output); |
| |
| // ValueToString stringifies a PKCS #11 attribute value. |
| std::string ValueToString(CK_ATTRIBUTE_TYPE attribute, |
| const std::vector<uint8_t>& value); |
| |
| // PrintAttributes parses serialized attributes and prints in the form: |
| // "{attribute1[=value1], attribute2[=value2]}". |
| std::string PrintAttributes(const std::vector<uint8_t>& serialized, |
| bool is_value_enabled); |
| |
| // PrintIntVector prints a vector in array literal form. E.g. "{0, 1, 2}". |
| // ** A static cast to 'int' must be possible for type T. |
| template <class T> |
| std::string PrintIntVector(const std::vector<T>& v) { |
| std::stringstream ss; |
| ss << "{"; |
| for (size_t i = 0; i < v.size(); i++) { |
| if (i > 0) |
| ss << ", "; |
| ss << static_cast<int>(v[i]); |
| } |
| ss << "}"; |
| return ss.str(); |
| } |
| |
| // This macro logs the current function name and the CK_RV value provided. |
| #define LOG_CK_RV(value) \ |
| LOG(ERROR) << __func__ << " - " << chaps::CK_RVToString(value); |
| |
| // This macro is a conditional version of LOG_CK_RV which will log only if the |
| // value is not CKR_OK. |
| #define LOG_CK_RV_ERR(value) \ |
| LOG_IF(ERROR, ((value) != CKR_OK)) \ |
| << __func__ << " - " << chaps::CK_RVToString(value); |
| |
| // This macro logs and returns the given CK_RV value. |
| #define LOG_CK_RV_AND_RETURN(value) \ |
| { \ |
| LOG_CK_RV(value); \ |
| return (value); \ |
| } |
| |
| // This macro logs and returns the given CK_RV value if the given condition is |
| // true. |
| #define LOG_CK_RV_AND_RETURN_IF(condition, value) \ |
| if (condition) \ |
| LOG_CK_RV_AND_RETURN(value) |
| |
| // This macro logs and returns the given CK_RV value if the value is not CKR_OK. |
| #define LOG_CK_RV_AND_RETURN_IF_ERR(value) \ |
| LOG_CK_RV_AND_RETURN_IF((value) != CKR_OK, value) |
| |
| // This function constructs a string object from a CK_UTF8CHAR array. The array |
| // does not need to be NULL-terminated. If buffer is NULL, an empty string will |
| // be returned. |
| inline std::string ConvertCharBufferToString(CK_UTF8CHAR_PTR buffer, |
| size_t buffer_size) { |
| if (!buffer) |
| return std::string(); |
| return std::string(reinterpret_cast<char*>(buffer), buffer_size); |
| } |
| |
| // This function constructs a string object from a CK_BYTE array. The array |
| // does not need to be NULL-terminated. If buffer is NULL, an empty string will |
| // be returned. |
| inline std::string ConvertByteBufferToString(CK_BYTE_PTR buffer, |
| CK_ULONG buffer_size) { |
| if (!buffer) |
| return std::string(); |
| return std::string(reinterpret_cast<char*>(buffer), buffer_size); |
| } |
| |
| // This function converts a C string to a CK_UTF8CHAR_PTR which points to the |
| // same buffer. |
| inline CK_UTF8CHAR_PTR ConvertStringToCharBuffer(const char* str) { |
| return reinterpret_cast<CK_UTF8CHAR_PTR>(const_cast<char*>(str)); |
| } |
| |
| // This function converts a C string to a uint8_t* which points to the same |
| // buffer. |
| inline uint8_t* ConvertStringToByteBuffer(const char* str) { |
| return reinterpret_cast<uint8_t*>(const_cast<char*>(str)); |
| } |
| |
| // This function changes the container class for an array of bytes from string |
| // to vector. |
| inline std::vector<uint8_t> ConvertByteStringToVector(const std::string& s) { |
| const uint8_t* front = reinterpret_cast<const uint8_t*>(s.data()); |
| return std::vector<uint8_t>(front, front + s.length()); |
| } |
| |
| // This function changes the container class for an array of bytes from vector |
| // to string. |
| inline std::string ConvertByteVectorToString(const std::vector<uint8_t>& v) { |
| const char* front = reinterpret_cast<const char*>(v.data()); |
| return std::string(front, v.size()); |
| } |
| |
| // This function constructs a vector object from a CK_BYTE array.If buffer is |
| // NULL, an empty vector will be returned. |
| inline std::vector<uint8_t> ConvertByteBufferToVector(CK_BYTE_PTR buffer, |
| CK_ULONG buffer_size) { |
| if (!buffer) |
| return std::vector<uint8_t>(); |
| return std::vector<uint8_t>(buffer, buffer + buffer_size); |
| } |
| |
| // This function returns a value composed of the bytes in the given string. |
| // It only accepts strings whose length is the same as the size of the given |
| // type. The type must be default-constructible. |
| template <typename T> |
| T ExtractFromByteString(const std::string& s) { |
| CHECK(s.length() == sizeof(T)); |
| T ret; |
| memcpy(&ret, s.data(), sizeof(ret)); |
| return ret; |
| } |
| |
| // This class preserves a variable that needs to be temporarily converted to |
| // another type. |
| template <class PreservedType, class TempType> |
| class PreservedValue { |
| public: |
| explicit PreservedValue(PreservedType* value) { |
| CHECK(value); |
| preserved_ = value; |
| temp_ = static_cast<TempType>(*value); |
| } |
| ~PreservedValue() { *preserved_ = static_cast<PreservedType>(temp_); } |
| // Allow an implicit cast to a pointer to the temporary value. |
| operator TempType*() { return &temp_; } |
| |
| private: |
| PreservedType* preserved_; |
| TempType temp_; |
| }; |
| |
| typedef PreservedValue<CK_ULONG, uint64_t> PreservedCK_ULONG; |
| typedef PreservedValue<uint64_t, CK_ULONG> PreservedUint64_t; |
| |
| class PreservedByteVector { |
| public: |
| explicit PreservedByteVector(std::vector<uint8_t>* value) { |
| CHECK(value); |
| preserved_ = value; |
| temp_ = ConvertByteVectorToString(*value); |
| } |
| ~PreservedByteVector() { *preserved_ = ConvertByteStringToVector(temp_); } |
| // Allow an implicit cast to a string pointer. |
| operator std::string*() { return &temp_; } |
| |
| private: |
| std::vector<uint8_t>* preserved_; |
| std::string temp_; |
| }; |
| |
| // Computes and returns a SHA-1 hash of the given input. |
| std::string Sha1(const std::string& input); |
| brillo::SecureBlob Sha1(const brillo::SecureBlob& input); |
| |
| // Computes and returns a SHA-256 hash of the given input. |
| brillo::SecureBlob Sha256(const brillo::SecureBlob& input); |
| |
| // Computes and returns a SHA-512 hash of the given input. |
| brillo::SecureBlob Sha512(const brillo::SecureBlob& input); |
| |
| // Initializes the OpenSSL library on construction and terminates the library on |
| // destruction. |
| class ScopedOpenSSL { |
| public: |
| ScopedOpenSSL(); |
| ~ScopedOpenSSL(); |
| }; |
| |
| // Returns a description of the OpenSSL error stack. |
| std::string GetOpenSSLError(); |
| |
| // Computes a message authentication code using HMAC and SHA-512. |
| std::string HmacSha512(const std::string& input, const brillo::SecureBlob& key); |
| |
| // Performs AES-256 encryption / decryption in CBC mode with PKCS padding. If |
| // 'iv' is left empty, a random IV will be generated and appended to the cipher- |
| // text on encryption. |
| bool RunCipher(bool is_encrypt, |
| const brillo::SecureBlob& key, |
| const std::string& iv, |
| const std::string& input, |
| std::string* output); |
| |
| // Returns true if the given attribute type has an integral value. |
| bool IsIntegralAttribute(CK_ATTRIBUTE_TYPE type); |
| |
| // TODO(crbug/916023): Move pure OpenSSL conversion wrappers to a cross daemon |
| // library. |
| // |
| // OpenSSL type <--> raw data string |
| // |
| |
| // Convert OpenSSL BIGNUM |bignum| to string. |
| // Padding the result to at least |pad_to_length| bytes long. |
| // Return empty string on error. |
| std::string ConvertFromBIGNUM(const BIGNUM* bignum, int pad_to_length = 0); |
| |
| // Convert string |big_integer| into pre-allocated OpenSSL BIGNUM. |
| // Returns false if big_integer is empty, b is nullptr, or conversion fails. |
| bool ConvertToBIGNUM(const std::string& big_integer, BIGNUM* b); |
| |
| // Convert the public key consisting of |modulus| and |exponent| to an RSA |
| // object and return it on success, otherwise, return nullptr. |
| crypto::ScopedRSA NumberToScopedRsa(const std::string& modulus, |
| const std::string& exponent); |
| |
| // |
| // OpenSSL type <--> DER-encoded string |
| // |
| |
| // Get the ECParamters from |key| and DER-encode to a string. |
| std::string GetECParametersAsString(const EC_KEY* key); |
| // Get the EC_Point from |key| and DER-encode to a string. |
| std::string GetECPointAsString(const EC_KEY* key); |
| |
| // |
| // OpenSSL type <--> PKCS #11 Attributes |
| // |
| |
| // Create a OpenSSL EC_KEY from a CKA_EC_PARAMS string (which is compatible |
| // with DER-encoded OpenSSL ECParameters) |
| crypto::ScopedEC_KEY CreateECCKeyFromEC_PARAMS(const std::string& ec_params); |
| |
| // In OpenSSL 1.1, i2o_ECPublicKey now takes a const EC_KEY *, which breaks |
| // the function signature expectation of ConvertOpenSSLObjectToString, so |
| // wrap it with a helper that still takes a non-const pointer. |
| static inline int i2o_ECPublicKey_nc(EC_KEY* key, unsigned char** buf) { |
| return i2o_ECPublicKey(key, buf); |
| } |
| |
| // Get the chaps internal digest algorithm type from PKCS#11 mechanism type. |
| chaps::DigestAlgorithm GetDigestAlgorithm(CK_MECHANISM_TYPE mechanism); |
| |
| // Return the OpenSSL Digest associated with the given DigestAlgorithm. |
| const EVP_MD* GetOpenSSLDigest(DigestAlgorithm alg); |
| |
| // Return the OpenSSL Digest associated with the given PKCS#11 Mechanism. |
| const EVP_MD* GetOpenSSLDigestForMechanism(CK_MECHANISM_TYPE mechanism); |
| |
| // Return the RSA padding scheme for the given |mechanism|. |
| RsaPaddingScheme GetSigningSchemeForMechanism( |
| const CK_MECHANISM_TYPE mechanism); |
| |
| // Return the OpenSSL Digest associated with the given PKCS#11 MGF function |
| // identifier. |
| const EVP_MD* GetOpenSSLDigestForMGF(const CK_RSA_PKCS_MGF_TYPE mgf); |
| |
| // This method parse the PKCS#11 mechanism parameters specified in |
| // |mechanism_parameter| and interpret it as CK_RSA_PKCS_PSS_PARAMS and check |
| // its sanity. Return false on error. Otherwise, return true and set the 3 |
| // output parameters if the check is successful. |
| bool ParseRSAPSSParams(const std::string& mechanism_parameter, |
| const CK_RSA_PKCS_PSS_PARAMS** pss_params_out, |
| const EVP_MD** mgf1_hash_out, |
| DigestAlgorithm* digest_algorithm_out); |
| |
| } // namespace chaps |
| |
| #endif // CHAPS_CHAPS_UTILITY_H_ |