|
| 1 | +/* |
| 2 | + * |
| 3 | + * Copyright (c) 2019 Google LLC. |
| 4 | + * All rights reserved. |
| 5 | + * |
| 6 | + * Licensed under the Apache License, Version 2.0 (the "License"); |
| 7 | + * you may not use this file except in compliance with the License. |
| 8 | + * You may obtain a copy of the License at |
| 9 | + * |
| 10 | + * http://www.apache.org/licenses/LICENSE-2.0 |
| 11 | + * |
| 12 | + * Unless required by applicable law or agreed to in writing, software |
| 13 | + * distributed under the License is distributed on an "AS IS" BASIS, |
| 14 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 15 | + * See the License for the specific language governing permissions and |
| 16 | + * limitations under the License. |
| 17 | + */ |
| 18 | + |
| 19 | +/** |
| 20 | + * @file |
| 21 | + * General RSA utility functions. |
| 22 | + * |
| 23 | + */ |
| 24 | + |
| 25 | +#include "RSA.h" |
| 26 | +#include <Weave/Core/WeaveEncoding.h> |
| 27 | +#include <Weave/Support/CodeUtils.h> |
| 28 | + |
| 29 | +#if WEAVE_WITH_OPENSSL |
| 30 | +#include <openssl/bio.h> |
| 31 | +#include <openssl/x509.h> |
| 32 | +#include <openssl/rsa.h> |
| 33 | +#endif |
| 34 | + |
| 35 | +namespace nl { |
| 36 | +namespace Weave { |
| 37 | +namespace Crypto { |
| 38 | + |
| 39 | +using namespace nl::Weave::TLV; |
| 40 | + |
| 41 | +/** |
| 42 | + * Compares with another RSA key. |
| 43 | + * |
| 44 | + * @param[in] other The EncodedRSAKey object with which key should be compared. |
| 45 | + * |
| 46 | + * @retval true The keys are equal. |
| 47 | + * @retval false The keys are not equal. |
| 48 | + */ |
| 49 | +bool EncodedRSAKey::IsEqual(const EncodedRSAKey& other) const |
| 50 | +{ |
| 51 | + return (Key != NULL && |
| 52 | + other.Key != NULL && |
| 53 | + Len == other.Len && |
| 54 | + memcmp(Key, other.Key, Len) == 0); |
| 55 | +} |
| 56 | + |
| 57 | +/** |
| 58 | + * Compares with another RSA signature. |
| 59 | + * |
| 60 | + * @param[in] other The EncodedRSASignature object with which signature should be compared. |
| 61 | + * |
| 62 | + * @retval true The signatures are equal. |
| 63 | + * @retval false The signatures are not equal. |
| 64 | + */ |
| 65 | +bool EncodedRSASignature::IsEqual(const EncodedRSASignature& other) const |
| 66 | +{ |
| 67 | + return (Sig != NULL && |
| 68 | + other.Sig != NULL && |
| 69 | + Len == other.Len && |
| 70 | + memcmp(Sig, other.Sig, Len) == 0); |
| 71 | +} |
| 72 | + |
| 73 | +/** |
| 74 | + * Reads the signature as a Weave RSASignature structure from the specified TLV reader. |
| 75 | + * |
| 76 | + * @param[in] reader The TLVReader object from which the encoded signature should |
| 77 | + * be read. |
| 78 | + * |
| 79 | + * @retval #WEAVE_NO_ERROR If the operation succeeded. |
| 80 | + * @retval other Other Weave error codes related to signature reading. |
| 81 | + */ |
| 82 | +WEAVE_ERROR EncodedRSASignature::ReadSignature(TLVReader& reader) |
| 83 | +{ |
| 84 | + WEAVE_ERROR err; |
| 85 | + |
| 86 | + VerifyOrExit(reader.GetType() == kTLVType_ByteString, err = WEAVE_ERROR_WRONG_TLV_TYPE); |
| 87 | + |
| 88 | + err = reader.GetDataPtr(const_cast<const uint8_t *&>(Sig)); |
| 89 | + SuccessOrExit(err); |
| 90 | + |
| 91 | + Len = reader.GetLength(); |
| 92 | + |
| 93 | +exit: |
| 94 | + return err; |
| 95 | +} |
| 96 | + |
| 97 | +#if WEAVE_WITH_OPENSSL |
| 98 | + |
| 99 | +static int ShaNIDFromSigAlgoOID(OID sigAlgoOID) |
| 100 | +{ |
| 101 | + int nid; |
| 102 | + |
| 103 | + // Current implementation only supports SHA256WithRSAEncryption signature algorithm. |
| 104 | + if (sigAlgoOID == ASN1::kOID_SigAlgo_SHA256WithRSAEncryption) |
| 105 | + nid = NID_sha256; |
| 106 | + else |
| 107 | + nid = NID_undef; |
| 108 | + |
| 109 | + return nid; |
| 110 | +} |
| 111 | + |
| 112 | +/** |
| 113 | + * Generate and encode a Weave RSA signature |
| 114 | + * |
| 115 | + * Computes an RSA signature using a given X509 encoded RSA private key and message hash |
| 116 | + * and writes the signature as a Weave RSASignature structure to the specified TLV writer |
| 117 | + * with the given tag. |
| 118 | + * |
| 119 | + * @param[in] sigAlgoOID Algorithm OID to be used to generate RSA signature. |
| 120 | + * @param[in] writer The TLVWriter object to which the encoded signature should |
| 121 | + * be written. |
| 122 | + * @param[in] tag TLV tag to be associated with the encoded signature structure. |
| 123 | + * @param[in] hash A buffer containing the hash of the data to be signed. |
| 124 | + * @param[in] hashLen The length in bytes of the data hash. |
| 125 | + * @param[in] keyDER A buffer containing the private key to be used to generate |
| 126 | + * the signature. The private key is expected to be encoded as |
| 127 | + * an X509 RSA private key structure. |
| 128 | + * @param[in] keyDERLen The length in bytes of the encoded private key. |
| 129 | + * |
| 130 | + * @retval #WEAVE_NO_ERROR If the operation succeeded. |
| 131 | + * @retval other Other Weave error codes related to decoding the private key, |
| 132 | + * generating the signature or encoding the signature. |
| 133 | + * |
| 134 | + */ |
| 135 | +WEAVE_ERROR GenerateAndEncodeWeaveRSASignature(OID sigAlgoOID, |
| 136 | + TLVWriter& writer, uint64_t tag, |
| 137 | + const uint8_t * hash, uint8_t hashLen, |
| 138 | + const uint8_t * keyDER, uint16_t keyDERLen) |
| 139 | +{ |
| 140 | + WEAVE_ERROR err; |
| 141 | + RSA *rsa = NULL; |
| 142 | + uint8_t sigBuf[EncodedRSASignature::kMaxValueLength]; |
| 143 | + uint32_t sigLen; |
| 144 | + EncodedRSASignature sig; |
| 145 | + int shaNID; |
| 146 | + |
| 147 | + shaNID = ShaNIDFromSigAlgoOID(sigAlgoOID); |
| 148 | + VerifyOrExit(shaNID != NID_undef, err = WEAVE_ERROR_UNSUPPORTED_SIGNATURE_TYPE); |
| 149 | + |
| 150 | + rsa = d2i_RSAPrivateKey(NULL, &keyDER, keyDERLen); |
| 151 | + VerifyOrExit(rsa != NULL, err = WEAVE_ERROR_NO_MEMORY); |
| 152 | + |
| 153 | + if (RSA_sign(shaNID, hash, hashLen, sigBuf, &sigLen, rsa) == 0) |
| 154 | + ExitNow(err = WEAVE_ERROR_NO_MEMORY); |
| 155 | + |
| 156 | + sig.Sig = sigBuf; |
| 157 | + sig.Len = sigLen; |
| 158 | + |
| 159 | + // Encode an RSASignature value into the supplied writer. |
| 160 | + err = sig.WriteSignature(writer, tag); |
| 161 | + SuccessOrExit(err); |
| 162 | + |
| 163 | +exit: |
| 164 | + if (NULL != rsa) RSA_free(rsa); |
| 165 | + |
| 166 | + return err; |
| 167 | +} |
| 168 | + |
| 169 | +/** |
| 170 | + * Verify a Weave RSA signature. |
| 171 | + * |
| 172 | + * Verifies an RSA signature using given data hash and an X509 encoded RSA certificate containing |
| 173 | + * the public key to be used to verify the signature. |
| 174 | + * |
| 175 | + * @param[in] sigAlgoOID Algorithm OID to be used to generate RSA signature. |
| 176 | + * @param[in] hash A buffer containing the hash of the data to be signed. |
| 177 | + * @param[in] hashLen The length in bytes of the data hash. |
| 178 | + * @param[in] sig Encoded RSA signature to be verified. |
| 179 | + * @param[in] certDER A buffer containing the certificate with public key to be used |
| 180 | + * to verify the signature. The certificate is expected to be DER |
| 181 | + * encoded an X509 RSA structure. |
| 182 | + * @param[in] certDERLen The length in bytes of the encoded certificate. |
| 183 | + * |
| 184 | + * @retval #WEAVE_NO_ERROR If the operation succeeded. |
| 185 | + */ |
| 186 | +WEAVE_ERROR VerifyRSASignature(OID sigAlgoOID, |
| 187 | + const uint8_t * hash, uint8_t hashLen, |
| 188 | + const EncodedRSASignature& sig, |
| 189 | + const uint8_t * certDER, uint16_t certDERLen) |
| 190 | +{ |
| 191 | + WEAVE_ERROR err = WEAVE_NO_ERROR; |
| 192 | + BIO *certBuf = NULL; |
| 193 | + X509 *cert = NULL; |
| 194 | + EVP_PKEY *pubKey = NULL; |
| 195 | + int shaNID; |
| 196 | + int res; |
| 197 | + |
| 198 | + shaNID = ShaNIDFromSigAlgoOID(sigAlgoOID); |
| 199 | + VerifyOrExit(shaNID != NID_undef, err = WEAVE_ERROR_UNSUPPORTED_SIGNATURE_TYPE); |
| 200 | + |
| 201 | + certBuf = BIO_new_mem_buf(certDER, certDERLen); |
| 202 | + VerifyOrExit(certBuf != NULL, err = WEAVE_ERROR_NO_MEMORY); |
| 203 | + |
| 204 | + cert = d2i_X509_bio(certBuf, NULL); |
| 205 | + VerifyOrExit(cert != NULL, err = WEAVE_ERROR_NO_MEMORY); |
| 206 | + |
| 207 | + pubKey = X509_get_pubkey(cert); |
| 208 | + VerifyOrExit(pubKey != NULL, err = WEAVE_ERROR_NO_MEMORY); |
| 209 | + |
| 210 | + // Verify that the public key from the certificate is an RSA key. |
| 211 | + VerifyOrExit(EVP_PKEY_RSA == EVP_PKEY_base_id(pubKey), err = WEAVE_ERROR_WRONG_KEY_TYPE); |
| 212 | + |
| 213 | + res = RSA_verify(shaNID, hash, hashLen, sig.Sig, sig.Len, EVP_PKEY_get1_RSA(pubKey)); |
| 214 | + VerifyOrExit(res == 1, err = WEAVE_ERROR_INVALID_SIGNATURE); |
| 215 | + |
| 216 | +exit: |
| 217 | + if (NULL != pubKey) EVP_PKEY_free(pubKey); |
| 218 | + if (NULL != cert) X509_free(cert); |
| 219 | + if (NULL != certBuf) BIO_free(certBuf); |
| 220 | + |
| 221 | + return err; |
| 222 | +} |
| 223 | + |
| 224 | +#endif // WEAVE_WITH_OPENSSL |
| 225 | + |
| 226 | +// ==================== Documentation for Inline Public Members ==================== |
| 227 | + |
| 228 | +/** |
| 229 | + * @fn WEAVE_ERROR EncodedRSASignature::WriteSignature(TLVWriter& writer, uint64_t tag) const |
| 230 | + * |
| 231 | + * Writes the signature as a Weave RSASignature structure to the specified TLV writer |
| 232 | + * with the given tag. |
| 233 | + * |
| 234 | + * @param[in] writer The TLVWriter object to which the encoded signature should |
| 235 | + * be written. |
| 236 | + * @param[in] tag TLV tag to be associated with the encoded signature structure. |
| 237 | +
|
| 238 | + * @retval #WEAVE_NO_ERROR If the operation succeeded. |
| 239 | + * @retval other Other Weave error codes related to signature writing. |
| 240 | + */ |
| 241 | + |
| 242 | +} // namespace Crypto |
| 243 | +} // namespace Weave |
| 244 | +} // namespace nl |
0 commit comments