//------------------------------------------------------------------------------
// File: SymKeys.hh
// Author: Andreas-Joachim Peters - CERN
//------------------------------------------------------------------------------
/************************************************************************
* EOS - the CERN Disk Storage System *
* Copyright (C) 2011 CERN/Switzerland *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see .*
************************************************************************/
//------------------------------------------------------------------------------
//! @file SymKeys.hh
//! @author Andreas-Joachim Peters
//! @brief Classs implementing a symmetric key store and CODEC facility
//------------------------------------------------------------------------------
#pragma once
#include "common/Namespace.hh"
#include "XrdOuc/XrdOucHash.hh"
#include "XrdOuc/XrdOucString.hh"
#include "XrdSys/XrdSysPthread.hh"
#include "XrdOuc/XrdOucEnv.hh"
#include "google/protobuf/message.h"
#include
#include
#include
#include
#include
#define EOSCOMMONSYMKEYS_GRACEPERIOD 5
#define EOSCOMMONSYMKEYS_DELETIONOFFSET 60
EOSCOMMONNAMESPACE_BEGIN
///-----------------------------------------------------------------------------
//! Class wrapping a symmetric key object and its encoding/decoding methods
///-----------------------------------------------------------------------------
class SymKey
{
public:
//----------------------------------------------------------------------------
//! Cipher encrypt using provided key
//!
//! @param data data to be encrypted
//! @param data_length length of the data
//! @param encrypted_data output encrypted data. It's not necessarily null
//! terminated and could contain embedded nulls.
//! @param encrypted_length output data length
//! @param key cipher key whose length must be SHA_DIGEST_LENGTH (20)
//!
//! @return true if encryption successful, otherwise false
//----------------------------------------------------------------------------
static bool CipherEncrypt(const char* data, ssize_t data_length,
char*& encrypted_data, ssize_t& encrypted_length,
char* key);
//----------------------------------------------------------------------------
//! Cipher decrypt using provided key
//!
//! @param encrypted_data input encrypted data
//! @param encrypted_length input data length
//! @param data decrypted data pointer for which the caller takes ownership
//! @param data_length length of the decrypted data
//! @param key cipher key whose length must be SHA_DIGEST_LENGTH (20)
//! @param noerror flag - if true disable error printing in the function
//!
//! @return true if decryption successful, otherwise false
//----------------------------------------------------------------------------
static bool CipherDecrypt(char* encrypted_data, ssize_t encrypted_length,
char*& data, ssize_t& data_length, char* key, bool noerror = false);
//----------------------------------------------------------------------------
//! Encrypt string and base64 encode it
//!
//! @param in input string
//! @param out output string
//! @param key symmetric key used for encryption
//!
//! @return true if successful, otherwise false
//----------------------------------------------------------------------------
static bool SymmetricStringEncrypt(XrdOucString& in, XrdOucString& out,
char* key);
//----------------------------------------------------------------------------
//! Decrypt base64 encoded string
//!
//! @param in base64 encoded encrypted string
//! @param out decoded and decrypted string
//! @param key symmetric key used for decryption
//!
//! @return true if successful, otherwise false
//----------------------------------------------------------------------------
static bool SymmetricStringDecrypt(XrdOucString& in, XrdOucString& out,
char* key);
//----------------------------------------------------------------------------
//! Create EOS specific capability and append to the output env object
//!
//! @param inenv input env object
//! @param outenv output env object
//! @param key key object used for encrypting the capability
//! @param validity duration for which the capability is valid
//!
//! @return 0 if successful, otherwise errno
//----------------------------------------------------------------------------
static int CreateCapability(XrdOucEnv* inenv, XrdOucEnv*& outenv,
SymKey* key, std::chrono::seconds validity);
//----------------------------------------------------------------------------
//! Extract EOS specific capability encoded in the env object
//!
//! @param inenv input env object
//! @param outenv output env object
//!
//! @return 0 if successful, otherwise errno
//----------------------------------------------------------------------------
static int ExtractCapability(XrdOucEnv* inenv, XrdOucEnv*& outenv);
//----------------------------------------------------------------------------
//! Compute the HMAC SHA-256 value of the data passed as input
//!
//! @param key the key to be used in the encryption process
//! @param data the message to be used as input
//! @param blockSize the size in which the input is divided before the
//! cryptographic function is applied ( 512 bits recommended )
//! @param resultSize the size of the result ( the size recommended by the
//! OpenSSL library is 256 bits = 32 bytes )
//!
//! @return hash-based message authentication code
//!
//----------------------------------------------------------------------------
static std::string HmacSha256(const std::string& key,
const std::string& data,
unsigned int blockSize = 64,
unsigned int resultSize = 32);
//----------------------------------------------------------------------------
//! Compute the SHA-256 value of the data passed as input
//!
//! @param data the message to be used as input
//! @param blockSize the size in which the input is divided before the
//! hash function is applied ( 512 bits recommended )
//!
//! @return hash message
//!
//----------------------------------------------------------------------------
static std::string Sha256(const std::string& data,
unsigned int blockSize = 32);
//----------------------------------------------------------------------------
//! Compute the HMAC SHA-1 value of the data passed as input
//!
//! @param data the message to be used as input
//! @param key the key to be used in the encryption process
//!
//! @return hash-based message authentication code
//!
//----------------------------------------------------------------------------
static std::string HmacSha1(std::string& data, const char* key = NULL);
//----------------------------------------------------------------------------
//! Base64 encode a string - base function
//!
//! @param decoded_bytes input data
//! @param decoded_length input data length
//! @param out encoded data in std::string
//!
//! @return true if succesful, otherwise false
//----------------------------------------------------------------------------
static bool Base64Encode(const char* decoded_bytes, ssize_t decoded_length,
std::string& out);
//----------------------------------------------------------------------------
//! Base64 encode a string - returning an XrdOucString object
//!
//! @param in input data
//! @param inline input data length
//! @param out encoded data
//!
//! @return true if succesful, otherwise false
//----------------------------------------------------------------------------
static bool Base64Encode(const char* in, unsigned int inlen, XrdOucString& out);
//----------------------------------------------------------------------------
//! Base64 decode data, output as char* and length
//!
//! @param in input data
//! @param out decoded data
//! @param outlen decoded data length
//----------------------------------------------------------------------------
static bool Base64Decode(const char* encoded_bytes, char*& decoded_bytes,
ssize_t& decoded_length);
//----------------------------------------------------------------------------
//! Base64 decode data, output as string
//!
//! @param in input data
//! @param out decoded data given as std::string
//----------------------------------------------------------------------------
static bool Base64Decode(const char* in, std::string& out);
//----------------------------------------------------------------------------
//! Base64 decode data stored in XrdOucString
//!
//! @param in input data
//! @param out decoded data
//! @param outlen decoded data length
//----------------------------------------------------------------------------
static bool Base64Decode(XrdOucString& in, char*& out, ssize_t& outlen);
//----------------------------------------------------------------------------
//! Decode a base64: prefixed string
//----------------------------------------------------------------------------
static bool DeBase64(XrdOucString& in, XrdOucString& out);
static bool DeBase64(const std::string& in, std::string& out);
//----------------------------------------------------------------------------
//! Encode a base64: prefixed string
//----------------------------------------------------------------------------
static bool Base64(XrdOucString& in, XrdOucString& out);
static bool Base64(std::string& in, std::string& out);
//----------------------------------------------------------------------------
//! Decode a zbase64: prefixed string
//----------------------------------------------------------------------------
static bool ZDeBase64(std::string& in, std::string& out);
//----------------------------------------------------------------------------
//! Encode a zbase64: prefixed string
//----------------------------------------------------------------------------
static bool ZBase64(std::string& in, std::string& out);
struct hmac_t {
void set(const std::string& secret, const std::string& key)
{
if (secret.length()) {
this->hmac = HmacSha256(secret, key);
this->key = hmac;
} else {
this->key = key;
}
}
hmac_t(const std::string& secret, const std::string& key)
{
set(secret, key);
}
hmac_t() {}
std::string key;
std::string hmac;
};
//----------------------------------------------------------------------------
//! Obfuscate a buffer based on offset and hmac
//----------------------------------------------------------------------------
static void ObfuscateBuffer(char* dst, const char* src, size_t size,
off_t offset, hmac_t& hmac);
//----------------------------------------------------------------------------
//! Unbfuscate a buffer based on offset and hmac
//----------------------------------------------------------------------------
static void UnobfuscateBuffer(char* buf, size_t size, off_t offset,
hmac_t& hmac);
//----------------------------------------------------------------------------
//! Retrieve a random cipher fitting input key
//----------------------------------------------------------------------------
static std::string RandomCipher(const std::string& key)
{
size_t keyblocks = 1;
if (key.length() > 36) {
keyblocks = key.length() / 36 + 1;
}
std::string skey;
for (size_t i = 0; i < keyblocks; i++) {
// create a random uuid
char suuid[40];
uuid_t uuid;
uuid_generate_random(uuid);
uuid_unparse(uuid, suuid);
for (size_t n = 0; n < strlen(suuid); n++) {
if (suuid[n] != '-') {
skey += suuid[n];
}
}
}
return skey;
}
//----------------------------------------------------------------------------
//! Serialise a Google Protobuf object and base64 encode the result
//!
//! @param msg generic Google Protobuf object
//! @param output protobuf serialised and base64 encoded
//!
//! @return true if successful, otherwise false
//----------------------------------------------------------------------------
static bool
ProtobufBase64Encode(const google::protobuf::Message* msg,
std::string& output);
//----------------------------------------------------------------------------
//!
//! Constructor for a symmetric key
//!
//! @param inkey binary key of SHA_DIGEST_LENGTH
//! @param invalidity unix time stamp when the key becomes invalid
//----------------------------------------------------------------------------
SymKey(const char* inkey, time_t invalidity);
//----------------------------------------------------------------------------
//! Destructor
//----------------------------------------------------------------------------
~SymKey() = default;
//----------------------------------------------------------------------------
//! Output a key and it's digest to stderr
//----------------------------------------------------------------------------
void
Print()
{
fprintf(stderr, "symkey: ");
for (int i = 0; i < SHA_DIGEST_LENGTH; i++) {
fprintf(stderr, "%x ", (unsigned char) key[i]);
}
fprintf(stderr, "digest: %s", keydigest64);
}
//----------------------------------------------------------------------------
//! Return the binary key
//----------------------------------------------------------------------------
inline const char* GetKey()
{
return key;
}
//----------------------------------------------------------------------------
//! Return the base64 encoded key
//----------------------------------------------------------------------------
inline const char* GetKey64()
{
return key64.c_str();
}
//----------------------------------------------------------------------------
//! Return the binary key digest
//----------------------------------------------------------------------------
inline const char* GetDigest()
{
return keydigest;
}
//----------------------------------------------------------------------------
//! Return the base64 encoded digest
//----------------------------------------------------------------------------
inline const char* GetDigest64()
{
return keydigest64;
}
//----------------------------------------------------------------------------
//! Return the expiration timestamp of the key
//----------------------------------------------------------------------------
inline time_t GetValidity()
{
return mValidity;
}
//----------------------------------------------------------------------------
//! Check if the key is still valid
//----------------------------------------------------------------------------
bool
IsValid()
{
if (!mValidity) {
return true;
} else {
return ((time(0) + EOSCOMMONSYMKEYS_GRACEPERIOD) > mValidity);
}
}
//----------------------------------------------------------------------------
//! Factory function to create a SymKey Object
//----------------------------------------------------------------------------
static SymKey*
Create(const char* inkey, time_t validity)
{
return new SymKey(inkey, validity);
}
private:
static XrdSysMutex msMutex; ///< mutex for protecting the access to OpenSSL
char key[SHA_DIGEST_LENGTH + 1]; //< the symmetric key in binary format
//! the digest of the key in binary format
char keydigest[SHA_DIGEST_LENGTH + 1];
//! the digest of the key in base64 format
char keydigest64[SHA_DIGEST_LENGTH * 2];
XrdOucString key64; //< the key in base64 format
time_t mValidity; //< unix time when the validity of the key stops
};
//------------------------------------------------------------------------------
//! Class providing a keystore for symmetric keys
//------------------------------------------------------------------------------
class SymKeyStore
{
private:
std::mutex mMutex;
XrdOucHash Store;
SymKey* currentKey;
public:
//-----------------------------------------------------------------------------
//! Constructor
//-----------------------------------------------------------------------------
SymKeyStore(): currentKey(nullptr) {}
//-----------------------------------------------------------------------------
//! Destructor
//-----------------------------------------------------------------------------
~SymKeyStore()
{
std::unique_lock scope_lock(mMutex);
Store.Purge();
}
//-----------------------------------------------------------------------------
//! Set a binary key and it's validity
//-----------------------------------------------------------------------------
SymKey* SetKey(const char* key, time_t validity);
//-----------------------------------------------------------------------------
//! Set a base64 key and it's validity
//-----------------------------------------------------------------------------
SymKey* SetKey64(const char* key64, time_t validity);
//-----------------------------------------------------------------------------
//! Get a base64 encoded key by digest from the store
//-----------------------------------------------------------------------------
SymKey* GetKey(const char* keydigest64);
//-----------------------------------------------------------------------------
//! Get last added valid key from the store
//-----------------------------------------------------------------------------
SymKey* GetCurrentKey();
};
extern SymKeyStore gSymKeyStore; //< Global SymKey store singleton
EOSCOMMONNAMESPACE_END