poco/Crypto/src/RSACipherImpl.cpp
2011-09-14 16:41:40 +00:00

341 lines
7.8 KiB
C++

//
// RSACipherImpl.cpp
//
// $Id: //poco/1.4/Crypto/src/RSACipherImpl.cpp#1 $
//
// Library: Crypto
// Package: RSA
// Module: RSACipherImpl
//
// Copyright (c) 2008, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// Permission is hereby granted, free of charge, to any person or organization
// obtaining a copy of the software and accompanying documentation covered by
// this license (the "Software") to use, reproduce, display, distribute,
// execute, and transmit the Software, and to prepare derivative works of the
// Software, and to permit third-parties to whom the Software is furnished to
// do so, all subject to the following:
//
// The copyright notices in the Software and this entire statement, including
// the above license grant, this restriction and the following disclaimer,
// must be included in all copies of the Software, in whole or in part, and
// all derivative works of the Software, unless such copies or derivative
// works are solely in the form of machine-executable object code generated by
// a source language processor.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
#include "Poco/Crypto/RSACipherImpl.h"
#include "Poco/Crypto/CryptoTransform.h"
#include "Poco/Exception.h"
#include <openssl/err.h>
#include <openssl/rsa.h>
#include <cstring>
namespace Poco {
namespace Crypto {
namespace
{
void throwError()
{
unsigned long err;
std::string msg;
while ((err = ERR_get_error()))
{
if (!msg.empty())
msg.append("; ");
msg.append(ERR_error_string(err, 0));
}
throw Poco::IOException(msg);
}
int mapPaddingMode(RSAPaddingMode paddingMode)
{
switch (paddingMode)
{
case RSA_PADDING_PKCS1:
return RSA_PKCS1_PADDING;
case RSA_PADDING_PKCS1_OAEP:
return RSA_PKCS1_OAEP_PADDING;
case RSA_PADDING_SSLV23:
return RSA_SSLV23_PADDING;
case RSA_PADDING_NONE:
return RSA_NO_PADDING;
default:
poco_bugcheck();
return RSA_NO_PADDING;
}
}
class RSAEncryptImpl: public CryptoTransform
{
public:
RSAEncryptImpl(const RSA* pRSA, RSAPaddingMode paddingMode);
~RSAEncryptImpl();
std::size_t blockSize() const;
std::size_t maxDataSize() const;
std::streamsize transform(
const unsigned char* input,
std::streamsize inputLength,
unsigned char* output,
std::streamsize outputLength);
std::streamsize finalize(unsigned char* output, std::streamsize length);
private:
const RSA* _pRSA;
RSAPaddingMode _paddingMode;
std::streamsize _pos;
unsigned char* _pBuf;
};
RSAEncryptImpl::RSAEncryptImpl(const RSA* pRSA, RSAPaddingMode paddingMode):
_pRSA(pRSA),
_paddingMode(paddingMode),
_pos(0),
_pBuf(0)
{
_pBuf = new unsigned char[blockSize()];
}
RSAEncryptImpl::~RSAEncryptImpl()
{
delete [] _pBuf;
}
std::size_t RSAEncryptImpl::blockSize() const
{
return RSA_size(_pRSA);
}
std::size_t RSAEncryptImpl::maxDataSize() const
{
std::size_t size = blockSize();
switch (_paddingMode)
{
case RSA_PADDING_PKCS1:
case RSA_PADDING_SSLV23:
size -= 11;
break;
case RSA_PADDING_PKCS1_OAEP:
size -= 41;
break;
default:
break;
}
return size;
}
std::streamsize RSAEncryptImpl::transform(
const unsigned char* input,
std::streamsize inputLength,
unsigned char* output,
std::streamsize outputLength)
{
// always fill up the buffer before writing!
std::streamsize maxSize = static_cast<std::streamsize>(maxDataSize());
std::streamsize rsaSize = static_cast<std::streamsize>(blockSize());
poco_assert_dbg(_pos <= maxSize);
poco_assert (outputLength >= rsaSize);
int rc = 0;
while (inputLength > 0)
{
// check how many data bytes we are missing to get the buffer full
poco_assert_dbg (maxSize >= _pos);
std::streamsize missing = maxSize - _pos;
if (missing == 0)
{
poco_assert (outputLength >= rsaSize);
int n = RSA_public_encrypt(static_cast<int>(maxSize), _pBuf, output, const_cast<RSA*>(_pRSA), mapPaddingMode(_paddingMode));
if (n == -1)
throwError();
rc += n;
output += n;
outputLength -= n;
_pos = 0;
}
else
{
if (missing > inputLength)
missing = inputLength;
std::memcpy(_pBuf + _pos, input, static_cast<std::size_t>(missing));
input += missing;
_pos += missing;
inputLength -= missing;
}
}
return rc;
}
std::streamsize RSAEncryptImpl::finalize(unsigned char* output, std::streamsize length)
{
poco_assert (length >= blockSize());
poco_assert (_pos <= maxDataSize());
int rc = 0;
if (_pos > 0)
{
rc = RSA_public_encrypt(static_cast<int>(_pos), _pBuf, output, const_cast<RSA*>(_pRSA), mapPaddingMode(_paddingMode));
if (rc == -1) throwError();
}
return rc;
}
class RSADecryptImpl: public CryptoTransform
{
public:
RSADecryptImpl(const RSA* pRSA, RSAPaddingMode paddingMode);
~RSADecryptImpl();
std::size_t blockSize() const;
std::streamsize transform(
const unsigned char* input,
std::streamsize inputLength,
unsigned char* output,
std::streamsize outputLength);
std::streamsize finalize(
unsigned char* output,
std::streamsize length);
private:
const RSA* _pRSA;
RSAPaddingMode _paddingMode;
std::streamsize _pos;
unsigned char* _pBuf;
};
RSADecryptImpl::RSADecryptImpl(const RSA* pRSA, RSAPaddingMode paddingMode):
_pRSA(pRSA),
_paddingMode(paddingMode),
_pos(0),
_pBuf(0)
{
_pBuf = new unsigned char[blockSize()];
}
RSADecryptImpl::~RSADecryptImpl()
{
delete [] _pBuf;
}
std::size_t RSADecryptImpl::blockSize() const
{
return RSA_size(_pRSA);
}
std::streamsize RSADecryptImpl::transform(
const unsigned char* input,
std::streamsize inputLength,
unsigned char* output,
std::streamsize outputLength)
{
// always fill up the buffer before decrypting!
std::streamsize rsaSize = static_cast<std::streamsize>(blockSize());
poco_assert_dbg(_pos <= rsaSize);
poco_assert (outputLength >= rsaSize);
int rc = 0;
while (inputLength > 0)
{
// check how many data bytes we are missing to get the buffer full
poco_assert_dbg (rsaSize >= _pos);
std::streamsize missing = rsaSize - _pos;
if (missing == 0)
{
int tmp = RSA_private_decrypt(static_cast<int>(rsaSize), _pBuf, output, const_cast<RSA*>(_pRSA), mapPaddingMode(_paddingMode));
if (tmp == -1)
throwError();
rc += tmp;
output += tmp;
outputLength -= tmp;
_pos = 0;
}
else
{
if (missing > inputLength)
missing = inputLength;
std::memcpy(_pBuf + _pos, input, static_cast<std::size_t>(missing));
input += missing;
_pos += missing;
inputLength -= missing;
}
}
return rc;
}
std::streamsize RSADecryptImpl::finalize(unsigned char* output, std::streamsize length)
{
poco_assert (length >= blockSize());
int rc = 0;
if (_pos > 0)
{
rc = RSA_private_decrypt(static_cast<int>(_pos), _pBuf, output, const_cast<RSA*>(_pRSA), mapPaddingMode(_paddingMode));
if (rc == -1)
throwError();
}
return rc;
}
}
RSACipherImpl::RSACipherImpl(const RSAKey& key, RSAPaddingMode paddingMode):
_key(key),
_paddingMode(paddingMode)
{
}
RSACipherImpl::~RSACipherImpl()
{
}
CryptoTransform* RSACipherImpl::createEncryptor()
{
return new RSAEncryptImpl(_key.impl()->getRSA(), _paddingMode);
}
CryptoTransform* RSACipherImpl::createDecryptor()
{
return new RSADecryptImpl(_key.impl()->getRSA(), _paddingMode);
}
} } // namespace Poco::Crypto