blob: ff145efb60af6f4242fe72ad6588a06a9382645b [file] [log] [blame]
/*
* Copyright 2014 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef SECUREMESSAGE_CRYPTO_OPS_H_
#define SECUREMESSAGE_CRYPTO_OPS_H_
#include <cstdint>
#include <memory>
#include <string>
#include "securemessage/byte_buffer.h"
#include "securemessage/common.h"
namespace securemessage {
//
// Encapsulates the cryptographic operations used by the {@code SecureMessage*}
// classes.
//
class CryptoOps {
public:
//
// Enum of supported signature schemes.
//
enum SigType {
HMAC_SHA256,
ECDSA_P256_SHA256,
RSA2048_SHA256,
SIG_TYPE_END // used for testing
};
//
// Enum of supported encryption types
//
enum EncType {
NONE,
AES_256_CBC,
ENC_TYPE_END // used for testing
};
//
// Key Algorithms we support
//
enum KeyAlgorithm {
AES_256_KEY,
ECDSA_KEY,
RSA_KEY
};
//
// Key Types we believe to exist
//
enum KeyType {
PUBLIC,
PRIVATE,
SECRET
};
//
// Represents a base key class, SecretKey and PublicKey are derived from this
// class
//
class Key {
public:
//
// The default constructor is deleted so that class consumers cannot create
// a generic key class. They must use a particular key type class.
//
Key() = delete;
ByteBuffer data() { return data_; }
const ByteBuffer& data() const { return data_; }
KeyAlgorithm algorithm() const { return algorithm_; }
KeyType type() const { return type_; }
protected:
//
// Constructor cannot be called so that consumers can't simply create a
// generic key. Instead, they must use a particular key type class.
//
Key(const ByteBuffer& data, KeyAlgorithm algorithm, KeyType type) {
data_ = data;
algorithm_ = algorithm;
type_ = type;
}
ByteBuffer data_;
KeyAlgorithm algorithm_;
KeyType type_;
};
//
// We have three key types: Public, Private, and Secret (symmetric)
//
class PublicKey : public Key {
public:
PublicKey(const string& data, KeyAlgorithm algorithm)
: Key(ByteBuffer(data), algorithm, PUBLIC) {}
PublicKey(const ByteBuffer& data, KeyAlgorithm algorithm)
: Key(data, algorithm, PUBLIC) {}
};
class PrivateKey : public Key {
public:
PrivateKey(const string& data, KeyAlgorithm algorithm)
: Key(ByteBuffer(data), algorithm, PRIVATE) {}
PrivateKey(const ByteBuffer& data, KeyAlgorithm algorithm)
: Key(data, algorithm, PRIVATE) {}
};
class SecretKey : public Key {
public:
SecretKey(const string& data, KeyAlgorithm algorithm)
: Key(ByteBuffer(data), algorithm, SECRET) {}
SecretKey(const ByteBuffer& data, KeyAlgorithm algorithm)
: Key(data, algorithm, SECRET) {}
};
struct KeyPair {
std::unique_ptr<PrivateKey> private_key;
std::unique_ptr<PublicKey> public_key;
KeyPair(std::unique_ptr<PublicKey> pub,
std::unique_ptr<PrivateKey> priv) {
public_key = std::move(pub);
private_key = std::move(priv);
}
};
//
// Implements HKDF (RFC 5869) with the SHA-256 hash and a 256-bit output key
// length.
//
// @param inputKeyMaterial master key from which to derive sub-keys
// @param salt a (public) randomly generated 256-bit input that can be re-used
// @param info arbitrary information that is bound to the derived key (i.e.,
// used in its creation)
// @return a std::unique_ptr<string> holding the derived key bytes =
// HKDF-SHA256(inputKeyMaterial, salt, info) on success or nullptr on error
//
static std::unique_ptr<string> Hkdf(const string& inputKeyMaterial,
const string& salt,
const string& info);
//
// A key derivation function specific to this library, which accepts a {@code
// masterKey} and an arbitrary {@code purpose} describing the intended
// application of the derived sub-key, and produces a derived AES - 256 key
// safe to use as if it were independent of any other derived key which used
// a different {@code purpose}.
//
// @param masterKey any key suitable for use with HmacSHA256
// @param purpose a UTF-8 encoded string describing the intended purpose of
// derived key
// @return a derived Key suitable for use with AES-256
//
static std::unique_ptr<SecretKey> DeriveAes256KeyFor(
const SecretKey& masterKey, const string& purpose);
//
// SHA 256 length, in bytes
//
static const unsigned int kSha256DigestSize = 32;
//
// Salt length, in bytes
//
static const unsigned int kSaltSize = 32;
// AES 256 key length, in bytes
static const unsigned int kAesKeySize = 32;
//
// Truncated hash output length, in bytes.
//
static const unsigned int kDigestLength = 20;
//
// Computes a collision-resistant hash of {@link #kDigestLength} bytes
// (using a truncated SHA-256 output).
//
// @return a std::unique_ptr holding the digest or a nullptr on error
//
static std::unique_ptr<string> Digest(const string& data);
//
// Decrypts {@code ciphertext} using the algorithm specified in {@code
// encType}, with the specified {@code iv} and {@code decryptionKey}.
//
// @return a std::unique_ptr holding the decrypted data or a nullptr on error
//
static std::unique_ptr<string> Decrypt(const SecretKey& decryptionKey,
EncType encType,
const string& iv,
const string& ciphertext);
//
// Encrypts {@code plaintext} using the algorithm specified in {@code
// encType}, with the specified * {@code iv} and {@code encryptionKey}.
//
// @param rng source of randomness to be used with the specified cipher, if
// necessary
// @return a std::unique_ptr holding the encrypted data or nullptr on error
static std::unique_ptr<string> Encrypt(const SecretKey& encryptionKey,
EncType encType,
const string& iv,
const string& plaintext);
//
// Runs the Diffie-Hellman algorithm.
//
// @param private_key must have the same algorithm as the peer_key
// @param peer_key must be the matching public key
// @return an AES secret key created from the SHA-256 of the secret created
// here or nullptr on error.
//
static std::unique_ptr<SecretKey> KeyAgreementSha256(
const PrivateKey& private_key,
const PublicKey& peer_key);
//
// Generate a random IV appropriate for use with the algorithm specified in
// {@code encType}.
//
// @return a freshly generated IV (a random byte sequence of appropriate
// length)
//
static std::unique_ptr<string> GenerateIv(EncType encType);
//
// Signs {@code data} using the algorithm specified by {@code sigType} with
// {@code signingKey}.
//
// @param rng is required for public key signature schemes
// @return a std::unique_ptr holding a string that represents the raw
// signature or a nullptr on error
//
static std::unique_ptr<string> Sign(SigType sigType,
const Key& signingKey,
const string& data);
//
// Verifies the {@code signature} on {@code data} using the algorithm
// specified by {@code sigType} with {@code verificationKey}.
//
// @return true iff the signature is verified
//
static bool Verify(SigType sigType,
const Key& verificationKey,
const string& signature,
const string& data);
//
// Imports a P256 EC Public Key using the x and y coordinates of the curve.
//
// @return nullptr if the point is invalid
//
static std::unique_ptr<PublicKey> ImportEcP256Key(const string& x,
const string& y);
//
// Exports the x and y coordinates of a P256 EC Public Key.
//
// @return false iff the key cannot be exported (for example, incorrect type)
//
static bool ExportEcP256Key(const PublicKey& key, string *x, string *y);
//
// Imports a 2048 bit RSA Public Key using the modulus {@code n} and exponent
// {@code e}.
//
// @return nullptr if the parameters are invalid
//
static std::unique_ptr<PublicKey> ImportRsa2048Key(const string& n,
int32_t e);
//
// Exports the modulus and exponent of a 2048 bit RSA Public Key.
//
// @return false iff the key cannot be exported (for example, incorrect type)
//
static bool ExportRsa2048Key(const PublicKey& key, string* n, int32_t* e);
//
// Returns true if a sigType is a public key scheme
//
static bool IsPublicKeyScheme(SigType sigType);
//
// Indicates whether a "tag" is needed next to the plaintext body inside the
// ciphertext, to prevent the same ciphertext from being reused with someone
// else's signature on it.
//
static bool TaggedPlaintextRequired(const Key& signing_key,
SigType sig_type,
const Key& encryption_key);
// Generates a 256 bit AES secret key
static std::unique_ptr<SecretKey> GenerateAes256SecretKey();
// Generates a P256 EC key pair
static std::unique_ptr<KeyPair> GenerateEcP256KeyPair();
// Generates a 2048 bit RSA key pair
static std::unique_ptr<KeyPair> GenerateRsa2048KeyPair();
private:
//
// @return SHA-256(UTF-8 encoded input)
//
// Will return a 32 byte ByteBuffer representing the SHA256 of the input bytes
// Will return a nullptr if an internal error occurs
//
static std::unique_ptr<ByteBuffer> Sha256(const ByteBuffer& message);
//
// @return SHA256HMAC of specified message using provided key or nullptr on
// error
static std::unique_ptr<ByteBuffer> Sha256hmac(const ByteBuffer& key,
const ByteBuffer& message);
//
// A salt value specific to this library, generated as
// SHA-256("SecureMessage")
//
// We have to initialize salt in crypto_ops.cc because C++ doesn't allow
// initialization and declaration in the same place.
//
static const uint8_t kSalt[kSaltSize];
//
// The HKDF (RFC 5869) extraction function, using the SHA-256 hash
// function. This function is used to pre-process the inputKeyMaterial and mix
// it with the salt, producing output suitable for use with HKDF expansion
// function (which produces the actual derived key).
//
// @see #hkdfSha256Expand(ByteBuffer, ByteBuffer)
// @return a std::unique_ptr<ByteBuffer> with HMAC-SHA256(salt,
// inputKeyMaterial) (salt is the "key" for the HMAC) on success or a nullptr
// on error
//
static std::unique_ptr<ByteBuffer> HkdfSha256Extract(
const ByteBuffer& inputKeyMaterial, const ByteBuffer& salt);
//
// Special case of HKDF (RFC 5869) expansion function, using the SHA-256
// hash function and allowing for a maximum output length of 256 bits.
//
// @param pseudoRandomKey should be generated by
// {@link #hkdfSha256Expand(ByteBuffer, ByteBuffer}
// @param info arbitrary information the derived key should be bound to
// @return a std::unique_ptr<ByteBuffer> holding derived key bytes =
// HMAC-SHA256(pseudoRandomKey, info | 0x01) on success or a nullptr on
// error
//
static std::unique_ptr<ByteBuffer> HkdfSha256Expand(
const ByteBuffer& pseudoRandomKey, const ByteBuffer& info);
//
// Performs AES 256 CBC decryption of {@code ciphertext} with the specified
// {@code iv} and {@code decryptionKey}. Assumes PKCS#5 padding is used
//
// @return a std::unique_ptr holding the plaintext (decrypted) data or a
// nullptr on error
//
static std::unique_ptr<ByteBuffer> Aes256CBCDecrypt(
const SecretKey& decryptionKey,
const ByteBuffer& iv,
const ByteBuffer& ciphertext);
//
// Performs AES 256 CBC encryption of {@code plaintext} with the specified
// {@code iv} and {@code encryptionKey}. PKCS#5 is used.
//
// @return a std::unique_ptr holding the ciphertext (encrypted) data or a
// nullptr on error
//
static std::unique_ptr<ByteBuffer> Aes256CBCEncrypt(
const SecretKey& encryptionKey,
const ByteBuffer& iv,
const ByteBuffer& ciphertext);
//
// Get the purpose of an encryption scheme. Basically a string representation
// of the enum.
//
static string GetPurpose(EncType encType);
//
// Get the purpose of a signature scheme. Basically a string representation
// of the enum.
//
static string GetPurpose(SigType sigType);
// Computes the ECDSA P256 SHA 256 MAC
//
// @param data the data to be signed
// @param signingKey the ECDSA private key used for signing.
// @return std::unique_ptr holding string that represents the MAC or nullptr
// on error
//
static std::unique_ptr<ByteBuffer> EcdsaP256Sha256Sign(
const PrivateKey& private_key,
const ByteBuffer& data);
// Verifies the ECDSA P256 SHA 256 signature
//
// @param signature to be verified
// @param data the data over which the signature was computed
// @param verificationKey the ECDSA public key used for verification.
// @return bool true if signature is valid, false otherwise
//
static bool EcdsaP256Sha256Verify(const PublicKey& public_key,
const ByteBuffer& signature,
const ByteBuffer& data);
//
// Runs the Elliptic Curve variant of the Diffie-Hellman algorithm.
//
// @param private_key must have the algorithm ECDSA_KEY
// @param peer_key must have the algorithm ECDSA_KEY
// @return an AES secret key created from the SHA-256 of the secret created
// by applying ECDH to the private_key and the peer_key.
//
static std::unique_ptr<ByteBuffer> EcdhKeyAgreement(
const PrivateKey& private_key,
const PublicKey& peer_key);
// Computes the RSA 2048 SHA 256 signature
//
// @param data the data to be signed
// @param signingKey the RSA Private key used to sign the data. The key
// should be in DER form.
// @return std::unique_ptr holding string that represents the MAC or nullptr
// on error
//
// TODO(aczeskis): refactor sign and verify to be virtual functions of key
// objects
//
static std::unique_ptr<ByteBuffer> Rsa2048Sha256Sign(
const PrivateKey& private_key, const ByteBuffer& data);
// Verifies the RSA 2048 SHA 256 MAC
//
// @param signature to be verified
// @param data the data over which the signature was computed
// @param verificationKey the RSA public key used for verification
// @return bool true if signature is valid, false otherwise
//
static bool Rsa2048Sha256Verify(const PublicKey& public_key,
const ByteBuffer& signature,
const ByteBuffer& data);
static bool IsValidEcP256CoordinateEncoding(const string& bytes);
static bool IsValidRsa2048ModulusEncoding(const string& bytes);
//
// Converts int32 value to an array of bytes represented as a string. The
// bytes are in big-endian order.
//
static string Int32BytesToString(int32_t value);
//
// Converts a string of bytes to an int32 value. Fails if the bytes do not fit
// in the int32 type. The bytes are in big-endian order.
//
static bool StringToInt32Bytes(const string& value, int32_t* result);
// For testing private helper functions
friend class CryptoOpsTest;
virtual ~CryptoOps();
};
} // namespace securemessage
#endif // SECUREMESSAGE_CRYPTO_OPS_H_