1966 lines
64 KiB
ObjectPascal
1966 lines
64 KiB
ObjectPascal
{******************************************************************************}
|
||
{ }
|
||
{ Library: Fundamentals 5.00 }
|
||
{ File name: flcCipherRSA.pas }
|
||
{ File version: 5.11 }
|
||
{ Description: RSA cipher routines }
|
||
{ }
|
||
{ Copyright: Copyright (c) 2008-2020, David J Butler }
|
||
{ All rights reserved. }
|
||
{ This file is licensed under the BSD License. }
|
||
{ See http://www.opensource.org/licenses/bsd-license.php }
|
||
{ Redistribution and use in source and binary forms, with }
|
||
{ or without modification, are permitted provided that }
|
||
{ the following conditions are met: }
|
||
{ Redistributions of source code must retain the above }
|
||
{ copyright notice, this list of conditions and the }
|
||
{ following disclaimer. }
|
||
{ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND }
|
||
{ CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED }
|
||
{ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED }
|
||
{ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A }
|
||
{ PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL }
|
||
{ THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, }
|
||
{ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR }
|
||
{ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, }
|
||
{ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF }
|
||
{ USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) }
|
||
{ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER }
|
||
{ IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING }
|
||
{ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE }
|
||
{ USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE }
|
||
{ POSSIBILITY OF SUCH DAMAGE. }
|
||
{ }
|
||
{ Github: https://github.com/fundamentalslib }
|
||
{ E-mail: fundamentals.library at gmail.com }
|
||
{ }
|
||
{ References: }
|
||
{ }
|
||
{ RFC 8017 - PKCS #1: RSA Cryptography Specifications Version 2.2 }
|
||
{ https://tools.ietf.org/html/rfc8017 }
|
||
{ }
|
||
{ }
|
||
{ Revision history: }
|
||
{ }
|
||
{ 2008/01/20 0.01 Initial development. }
|
||
{ 2010/08/04 0.02 Update for changes to HugeWord. }
|
||
{ 2010/08/04 0.03 EME_PKCS message encoding. }
|
||
{ 2010/08/09 0.04 EME_OAEP message encoding. }
|
||
{ 2010/08/10 4.05 Tests. }
|
||
{ 2010/12/01 4.06 RSAPublicKeyAssignBuf. }
|
||
{ 2015/03/31 4.07 RawByteString changes. }
|
||
{ 2015/06/08 4.08 RSASignMessage and RSACheckSignature. }
|
||
{ 2016/01/09 5.09 Revised for Fundamentals 5. }
|
||
{ 2018/07/17 5.10 Word32 changes. }
|
||
{ 2020/05/16 5.11 EMSA_PKCS1 message encoding. }
|
||
{ }
|
||
{******************************************************************************}
|
||
|
||
{$INCLUDE flcCipher.inc}
|
||
|
||
unit flcCipherRSA;
|
||
|
||
interface
|
||
|
||
uses
|
||
{ System }
|
||
SysUtils,
|
||
|
||
{ Fundamentals }
|
||
flcStdTypes,
|
||
flcHash,
|
||
flcHugeInt;
|
||
|
||
|
||
|
||
{ }
|
||
{ RSA }
|
||
{ }
|
||
type
|
||
ERSA = class(Exception);
|
||
|
||
TRSAPublicKey = record
|
||
KeyBits : Int32;
|
||
Modulus : HugeWord;
|
||
Exponent : HugeWord;
|
||
end;
|
||
|
||
TRSAPrivateKey = record
|
||
KeyBits : Int32;
|
||
Modulus : HugeWord;
|
||
Exponent : HugeWord; // d
|
||
PublicExponent : HugeWord; // e
|
||
Prime1 : HugeWord; // p
|
||
Prime2 : HugeWord; // q
|
||
Phi : HugeWord; // (p-1) * (q-1)
|
||
Exponent1 : HugeWord; // d mod (p - 1)
|
||
Exponent2 : HugeWord; // d mod (q - 1)
|
||
Coefficient : HugeWord; // (inverse of q) mod p
|
||
end;
|
||
|
||
TRSAMessage = HugeWord;
|
||
|
||
|
||
|
||
{ RSAPublicKey }
|
||
|
||
procedure RSAPublicKeyInit(var Key: TRSAPublicKey);
|
||
procedure RSAPublicKeyFinalise(var Key: TRSAPublicKey);
|
||
|
||
procedure RSAPublicKeyAssign(var KeyD: TRSAPublicKey; const KeyS: TRSAPublicKey);
|
||
procedure RSAPublicKeyAssignHex(var Key: TRSAPublicKey;
|
||
const KeyBits: Int32;
|
||
const HexMod, HexExp: String);
|
||
procedure RSAPublicKeyAssignBuf(var Key: TRSAPublicKey;
|
||
const KeyBits: Int32;
|
||
const ModBuf; const ModBufSize: Integer;
|
||
const ExpBuf; const ExpBufSize: Integer;
|
||
const ReverseByteOrder: Boolean);
|
||
procedure RSAPublicKeyAssignBufStr(var Key: TRSAPublicKey;
|
||
const KeyBits: Int32;
|
||
const ModBuf, ExpBuf: RawByteString);
|
||
|
||
|
||
|
||
{ RSAPrivateKey }
|
||
|
||
procedure RSAPrivateKeyInit(var Key: TRSAPrivateKey);
|
||
procedure RSAPrivateKeyFinalise(var Key: TRSAPrivateKey);
|
||
|
||
procedure RSAPrivateKeyAssign(var KeyD: TRSAPrivateKey; const KeyS: TRSAPrivateKey);
|
||
procedure RSAPrivateKeyAssignHex(var Key: TRSAPrivateKey;
|
||
const KeyBits: Int32;
|
||
const HexMod, HexExp: RawByteString);
|
||
procedure RSAPrivateKeyAssignBuf(var Key: TRSAPrivateKey;
|
||
const KeyBits: Int32;
|
||
const ModBuf; const ModBufSize: Integer;
|
||
const ExpBuf; const ExpBufSize: Integer;
|
||
const ReverseByteOrder: Boolean);
|
||
procedure RSAPrivateKeyAssignBufStr(var Key: TRSAPrivateKey;
|
||
const KeyBits: Int32;
|
||
const ModBuf, ExpBuf: RawByteString);
|
||
|
||
|
||
|
||
{ Generate }
|
||
|
||
type
|
||
TRSAGenerateCallback =
|
||
procedure (const CallbackData: NativeUInt; var Abort: Boolean);
|
||
ERSAGenerateAborted = class(ERSA);
|
||
|
||
procedure RSAGenerateKeys(const KeyBits: Integer;
|
||
var PrivateKey: TRSAPrivateKey;
|
||
var PublicKey: TRSAPublicKey;
|
||
const Callback: TRSAGenerateCallback = nil;
|
||
const CallbackData: NativeUInt = 0);
|
||
|
||
function RSACipherMessageBufSize(const KeySize: Integer): Integer;
|
||
|
||
|
||
|
||
{ EME_PKCS1 }
|
||
{ Used in RSAES-PKCS1-v1_5 }
|
||
|
||
procedure RSAEncodeMessageEME_PKCS1(
|
||
const KeyBits: Int32;
|
||
const Buf; const BufSize: Integer;
|
||
var EncodedMessage: TRSAMessage);
|
||
|
||
function RSADecodeMessageEME_PKCS1(
|
||
const KeyBits: Int32;
|
||
const EncodedMessage: HugeWord;
|
||
var Buf; const BufSize: Integer): Integer;
|
||
|
||
|
||
|
||
{ EME_OAEP }
|
||
{ Used in RSAES-OAEP }
|
||
|
||
procedure RSAEncodeMessageEME_OAEP(
|
||
const KeyBits: Int32;
|
||
const Buf; const BufSize: Integer;
|
||
var EncodedMessage: TRSAMessage);
|
||
|
||
function RSADecodeMessageEME_OAEP(
|
||
const KeyBits: Int32;
|
||
const EncodedMessage: HugeWord;
|
||
var Buf; const BufSize: Integer): Integer;
|
||
|
||
|
||
|
||
{ RSACipher Message }
|
||
|
||
function RSACipherMessageToBuf(
|
||
const KeyBits: Int32;
|
||
const CipherMessage: TRSAMessage;
|
||
var CipherBuf; const CipherBufSize: Integer): Integer;
|
||
|
||
procedure RSACipherBufToMessage(
|
||
const KeyBits: Int32;
|
||
const CipherBuf; const CipherBufSize: Integer;
|
||
var CipherMessage: TRSAMessage);
|
||
|
||
|
||
|
||
{ RSA Encryption Type }
|
||
|
||
type
|
||
TRSAEncryptionType = (
|
||
rsaetRSAES_PKCS1,
|
||
rsaetRSAES_OAEP
|
||
);
|
||
|
||
|
||
|
||
{ Encrypt }
|
||
|
||
procedure RSAEncryptMessage(
|
||
const PublicKey: TRSAPublicKey;
|
||
const PlainMessage: TRSAMessage;
|
||
var CipherMessage: TRSAMessage);
|
||
|
||
function RSAEncrypt(
|
||
const EncryptionType: TRSAEncryptionType;
|
||
const PublicKey: TRSAPublicKey;
|
||
const PlainBuf; const PlainBufSize: Integer;
|
||
var CipherBuf; const CipherBufSize: Integer): Integer;
|
||
|
||
function RSAEncryptStr(
|
||
const EncryptionType: TRSAEncryptionType;
|
||
const PublicKey: TRSAPublicKey;
|
||
const Plain: RawByteString): RawByteString;
|
||
|
||
|
||
|
||
{ Decrypt }
|
||
|
||
procedure RSADecryptMessage(
|
||
const PrivateKey: TRSAPrivateKey;
|
||
const CipherMessage: TRSAMessage;
|
||
var EncodedMessage: TRSAMessage);
|
||
|
||
function RSADecrypt(
|
||
const EncryptionType: TRSAEncryptionType;
|
||
const PrivateKey: TRSAPrivateKey;
|
||
const CipherBuf; const CipherBufSize: Integer;
|
||
var PlainBuf; const PlainBufSize: Integer): Integer;
|
||
|
||
function RSADecryptStr(
|
||
const EncryptionType: TRSAEncryptionType;
|
||
const PrivateKey: TRSAPrivateKey;
|
||
const Cipher: RawByteString): RawByteString;
|
||
|
||
|
||
|
||
{ Hash }
|
||
|
||
type
|
||
TRSAHashType = (
|
||
rsahfMD5,
|
||
rsahfSHA1,
|
||
rsahfSHA256,
|
||
rsahfSHA384,
|
||
rsahfSHA512
|
||
);
|
||
|
||
function RSAHashBuf(
|
||
const HashType: TRSAHashType;
|
||
const Buf; const BufSize: NativeInt;
|
||
const Digest; const DigestSize: Integer): Integer;
|
||
|
||
|
||
|
||
{ EMSA_PKCS1 }
|
||
|
||
procedure RSAEncodeMessageEMSA_PKCS1(
|
||
const KeyBits: Int32;
|
||
const HashType: TRSAHashType;
|
||
const Buf; const BufSize: NativeInt;
|
||
var EncodedMessage: TRSAMessage);
|
||
|
||
function RSADecodeMessageEMSA_PKCS1(
|
||
const KeyBits: Int32;
|
||
const EncodedMessage: HugeWord;
|
||
var HashType: TRSAHashType;
|
||
var Buf; const BufSize: Integer): Integer;
|
||
|
||
|
||
|
||
{ SignMsg }
|
||
|
||
procedure RSASignBufToMsg(const KeyBits: Integer; var Msg: HugeWord; const Buf; const BufSize: Integer);
|
||
|
||
function RSASignMsgToBuf(const KeyBits: Integer; var Buf; const BufSize: Integer; const Msg: HugeWord): Integer;
|
||
|
||
|
||
|
||
{ Sign / Verify }
|
||
|
||
type
|
||
TRSASignMessageType = (
|
||
rsastMessage,
|
||
rsastEMSA_PKCS1
|
||
);
|
||
|
||
function RSASignMessage(
|
||
const SignType: TRSASignMessageType;
|
||
const HashType: TRSAHashType;
|
||
const PrivateKey: TRSAPrivateKey;
|
||
const MessageBuf; const MessageBufSize: Integer;
|
||
var SignatureBuf; const SignatureBufSize: Integer): Integer;
|
||
|
||
function RSACheckSignature(
|
||
const SignType: TRSASignMessageType;
|
||
const PublicKey: TRSAPublicKey;
|
||
const MessageBuf; const MessageBufSize: NativeInt;
|
||
const SignatureBuf; const SignatureBufSize: Integer): Boolean;
|
||
|
||
|
||
|
||
{ }
|
||
{ Test }
|
||
{ }
|
||
{$IFDEF CIPHER_TEST}
|
||
procedure Test;
|
||
{$ENDIF}
|
||
{$IFDEF OS_WIN}
|
||
{$IFDEF CIPHER_PROFILE}
|
||
procedure Profile;
|
||
{$ENDIF}
|
||
{$ENDIF}
|
||
|
||
|
||
|
||
implementation
|
||
|
||
uses
|
||
{ System }
|
||
{$IFDEF OS_WIN}
|
||
{$IFDEF CIPHER_PROFILE}
|
||
Windows,
|
||
{$ENDIF}
|
||
{$ENDIF}
|
||
|
||
{ Fundamentals }
|
||
flcUtils,
|
||
flcRandom;
|
||
|
||
|
||
|
||
{ }
|
||
{ SecureClear }
|
||
{ }
|
||
procedure SecureHugeWordFinalise(var A: HugeWord);
|
||
begin
|
||
HugeWordClearAndFinalise(A);
|
||
end;
|
||
|
||
|
||
|
||
{ }
|
||
{ RSA }
|
||
{ }
|
||
const
|
||
SRSAInvalidKeySize = 'Invalid RSA key size';
|
||
SRSAInvalidBufferSize = 'Invalid RSA buffer size';
|
||
SRSAInvalidMessage = 'Invalid RSA message';
|
||
SRSAMessageTooLong = 'RSA message too long';
|
||
SRSAInvalidEncryptionType = 'Invalid RSA encryption type';
|
||
|
||
procedure RSAPublicKeyInit(var Key: TRSAPublicKey);
|
||
begin
|
||
Key.KeyBits := 0;
|
||
HugeWordInit(Key.Modulus);
|
||
HugeWordInit(Key.Exponent);
|
||
end;
|
||
|
||
procedure RSAPublicKeyFinalise(var Key: TRSAPublicKey);
|
||
begin
|
||
SecureHugeWordFinalise(Key.Exponent);
|
||
SecureHugeWordFinalise(Key.Modulus);
|
||
end;
|
||
|
||
procedure RSAPublicKeyAssign(var KeyD: TRSAPublicKey; const KeyS: TRSAPublicKey);
|
||
begin
|
||
KeyD.KeyBits := KeyS.KeyBits;
|
||
HugeWordAssign(KeyD.Modulus, KeyS.Modulus);
|
||
HugeWordAssign(KeyD.Exponent, KeyS.Exponent);
|
||
end;
|
||
|
||
procedure RSAPublicKeyAssignHex(var Key: TRSAPublicKey;
|
||
const KeyBits: Int32;
|
||
const HexMod, HexExp: String);
|
||
begin
|
||
if (KeyBits <= 0) or
|
||
(KeyBits mod HugeWordElementBits <> 0) then
|
||
raise ERSA.Create(SRSAInvalidKeySize);
|
||
Key.KeyBits := KeyBits;
|
||
HexToHugeWord(HexMod, Key.Modulus);
|
||
HexToHugeWord(HexExp, Key.Exponent);
|
||
if (HugeWordGetBitCount(Key.Modulus) > KeyBits) or
|
||
(HugeWordGetBitCount(Key.Exponent) > KeyBits) then
|
||
raise ERSA.Create(SRSAInvalidKeySize);
|
||
end;
|
||
|
||
procedure RSAPublicKeyAssignBuf(var Key: TRSAPublicKey;
|
||
const KeyBits: Int32;
|
||
const ModBuf; const ModBufSize: Integer;
|
||
const ExpBuf; const ExpBufSize: Integer;
|
||
const ReverseByteOrder: Boolean);
|
||
begin
|
||
if (KeyBits <= 0) or
|
||
(KeyBits mod HugeWordElementBits <> 0) then
|
||
raise ERSA.Create(SRSAInvalidKeySize);
|
||
Key.KeyBits := KeyBits;
|
||
HugeWordAssignBuf(Key.Modulus, ModBuf, ModBufSize, ReverseByteOrder);
|
||
HugeWordAssignBuf(Key.Exponent, ExpBuf, ExpBufSize, ReverseByteOrder);
|
||
end;
|
||
|
||
procedure RSAPublicKeyAssignBufStr(var Key: TRSAPublicKey;
|
||
const KeyBits: Int32;
|
||
const ModBuf, ExpBuf: RawByteString);
|
||
begin
|
||
RSAPublicKeyAssignBuf(Key, KeyBits, PByteChar(ModBuf)^, Length(ModBuf),
|
||
PByteChar(ExpBuf)^, Length(ExpBuf), True);
|
||
end;
|
||
|
||
procedure RSAPrivateKeyInit(var Key: TRSAPrivateKey);
|
||
begin
|
||
Key.KeyBits := 0;
|
||
HugeWordInit(Key.Modulus);
|
||
HugeWordInit(Key.Exponent);
|
||
HugeWordInit(Key.PublicExponent);
|
||
HugeWordInit(Key.Prime1);
|
||
HugeWordInit(Key.Prime2);
|
||
HugeWordInit(Key.Phi);
|
||
HugeWordInit(Key.Exponent1);
|
||
HugeWordInit(Key.Exponent2);
|
||
HugeWordInit(Key.Coefficient);
|
||
end;
|
||
|
||
procedure RSAPrivateKeyFinalise(var Key: TRSAPrivateKey);
|
||
begin
|
||
SecureHugeWordFinalise(Key.Coefficient);
|
||
SecureHugeWordFinalise(Key.Exponent2);
|
||
SecureHugeWordFinalise(Key.Exponent1);
|
||
SecureHugeWordFinalise(Key.Phi);
|
||
SecureHugeWordFinalise(Key.Prime2);
|
||
SecureHugeWordFinalise(Key.Prime1);
|
||
SecureHugeWordFinalise(Key.PublicExponent);
|
||
SecureHugeWordFinalise(Key.Exponent);
|
||
SecureHugeWordFinalise(Key.Modulus);
|
||
end;
|
||
|
||
procedure RSAPrivateKeyAssign(var KeyD: TRSAPrivateKey; const KeyS: TRSAPrivateKey);
|
||
begin
|
||
KeyD.KeyBits := KeyS.KeyBits;
|
||
HugeWordAssign(KeyD.Modulus, KeyS.Modulus);
|
||
HugeWordAssign(KeyD.Exponent, KeyS.Exponent);
|
||
HugeWordAssign(KeyD.PublicExponent, KeyS.PublicExponent);
|
||
HugeWordAssign(KeyD.Prime1, KeyS.Prime1);
|
||
HugeWordAssign(KeyD.Prime2, KeyS.Prime2);
|
||
HugeWordAssign(KeyD.Phi, KeyS.Phi);
|
||
HugeWordAssign(KeyD.Exponent1, KeyS.Exponent1);
|
||
HugeWordAssign(KeyD.Exponent2, KeyS.Exponent2);
|
||
HugeWordAssign(KeyD.Coefficient, KeyS.Coefficient);
|
||
end;
|
||
|
||
procedure RSAPrivateKeyAssignHex(var Key: TRSAPrivateKey;
|
||
const KeyBits: Int32;
|
||
const HexMod, HexExp: RawByteString);
|
||
begin
|
||
if (KeyBits <= 0) or
|
||
(KeyBits mod HugeWordElementBits <> 0) then
|
||
raise ERSA.Create(SRSAInvalidKeySize);
|
||
Key.KeyBits := KeyBits;
|
||
HexToHugeWordB(HexMod, Key.Modulus);
|
||
HexToHugeWordB(HexExp, Key.Exponent);
|
||
if (HugeWordGetBitCount(Key.Modulus) > KeyBits) or
|
||
(HugeWordGetBitCount(Key.Exponent) > KeyBits) then
|
||
raise ERSA.Create(SRSAInvalidKeySize);
|
||
end;
|
||
|
||
procedure RSAPrivateKeyAssignBuf(var Key: TRSAPrivateKey;
|
||
const KeyBits: Int32;
|
||
const ModBuf; const ModBufSize: Integer;
|
||
const ExpBuf; const ExpBufSize: Integer;
|
||
const ReverseByteOrder: Boolean);
|
||
begin
|
||
if (KeyBits <= 0) or
|
||
(KeyBits mod HugeWordElementBits <> 0) then
|
||
raise ERSA.Create(SRSAInvalidKeySize);
|
||
Key.KeyBits := KeyBits;
|
||
HugeWordAssignBuf(Key.Modulus, ModBuf, ModBufSize, ReverseByteOrder);
|
||
HugeWordAssignBuf(Key.Exponent, ExpBuf, ExpBufSize, ReverseByteOrder);
|
||
if (HugeWordGetBitCount(Key.Modulus) > KeyBits) or
|
||
(HugeWordGetBitCount(Key.Exponent) > KeyBits) then
|
||
raise ERSA.Create(SRSAInvalidKeySize);
|
||
end;
|
||
|
||
procedure RSAPrivateKeyAssignBufStr(var Key: TRSAPrivateKey;
|
||
const KeyBits: Int32;
|
||
const ModBuf, ExpBuf: RawByteString);
|
||
begin
|
||
RSAPrivateKeyAssignBuf(Key, KeyBits, PByteChar(ModBuf)^, Length(ModBuf),
|
||
PByteChar(ExpBuf)^, Length(ExpBuf), True);
|
||
end;
|
||
|
||
{ RSA Key Random Number }
|
||
{ Returns a random number for use in RSA key generation. }
|
||
procedure RSAKeyRandomNumber(const Bits: Integer; var A: HugeWord);
|
||
var
|
||
L : Integer;
|
||
begin
|
||
Assert(HugeWordElementBits >= 32);
|
||
if (Bits <= 0) or
|
||
(Bits mod HugeWordElementBits <> 0) then
|
||
raise ERSA.Create(SRSAInvalidKeySize);
|
||
// generate non-zero random number
|
||
L := Bits div HugeWordElementBits;
|
||
repeat
|
||
HugeWordRandom(A, L);
|
||
until not HugeWordIsZero(A);
|
||
// set least significant bit to make odd
|
||
HugeWordSetBit(A, 0);
|
||
// set one of the 15 most significant bits to ensure product is Bits * 2 large
|
||
// and this number allocates requested number of Bits in the HugeWord structure
|
||
HugeWordSetBit(A, Bits - RandomUniform(15) - 1);
|
||
// validate
|
||
Assert(HugeWordIsOdd(A));
|
||
Assert(HugeWordGetBitCount(A) = Bits);
|
||
end;
|
||
|
||
{ RSA Key Random Prime1 }
|
||
{ Returns the first of two random primes for use in RSA key generation. }
|
||
procedure RSAKeyRandomPrime1(const Bits: Integer; var P: HugeWord;
|
||
const Callback: TRSAGenerateCallback;
|
||
const CallbackData: NativeUInt);
|
||
var
|
||
Abort : Boolean;
|
||
begin
|
||
Abort := False;
|
||
repeat
|
||
RSAKeyRandomNumber(Bits, P);
|
||
// set the 2 most significant bits to:
|
||
// i) ensure that first prime is large enough so that there are
|
||
// enough smaller primes to choose from for the second prime;
|
||
// ii) the product is large enough
|
||
HugeWordSetBit(P, Bits - 1);
|
||
HugeWordSetBit(P, Bits - 2);
|
||
if Assigned(Callback) then
|
||
begin
|
||
Callback(CallbackData, Abort);
|
||
if Abort then
|
||
raise ERSAGenerateAborted.Create('Abort');
|
||
end;
|
||
until HugeWordIsPrime(P) <> pNotPrime;
|
||
end;
|
||
|
||
{ RSA Key Random Prime2 }
|
||
{ Returns the second of two random primes for use in RSA key generation. }
|
||
procedure RSAKeyRandomPrime2(const Bits: Integer; const P: HugeWord; var Q: HugeWord;
|
||
const Callback: TRSAGenerateCallback;
|
||
const CallbackData: NativeUInt);
|
||
var
|
||
Abort : Boolean;
|
||
L : HugeWord;
|
||
C : Integer;
|
||
N : Word32;
|
||
begin
|
||
Abort := False;
|
||
C := Bits div HugeWordElementBits;
|
||
HugeWordInit(L);
|
||
try
|
||
repeat
|
||
repeat
|
||
repeat
|
||
repeat
|
||
// choose a new random number with every iteration to maintain
|
||
// uniform distribution
|
||
RSAKeyRandomNumber(Bits, Q);
|
||
// "Numbers p and q should not be 'too close', lest the Fermat factorization for n be successful,
|
||
// if p - q, for instance is less than 2n^1/4 (which for even small 1024-bit values of n is 3<>10^77)
|
||
// solving for p and q is trivial"
|
||
HugeWordAssignOne(L);
|
||
HugeWordShl(L, (Bits div 4) + 1);
|
||
HugeWordAdd(L, Q);
|
||
until HugeWordCompare(P, L) > 0;
|
||
// ensure p > 2q - prevents certain attacks
|
||
HugeWordAssign(L, Q);
|
||
HugeWordShl1(L);
|
||
until HugeWordCompare(P, L) > 0;
|
||
// ensure N = P * Q large enough
|
||
N := Byte(HugeWordGetElement(P, C - 1) shr (HugeWordElementBits - 8)) *
|
||
Byte(HugeWordGetElement(Q, C - 1) shr (HugeWordElementBits - 8));
|
||
until N >= $0100;
|
||
if Assigned(Callback) then
|
||
begin
|
||
Callback(CallbackData, Abort);
|
||
if Abort then
|
||
raise ERSAGenerateAborted.Create('Abort');
|
||
end;
|
||
// ensure prime
|
||
until HugeWordIsPrime(Q) <> pNotPrime;
|
||
finally
|
||
SecureHugeWordFinalise(L);
|
||
end;
|
||
end;
|
||
|
||
{ RSA Key Random Prime Pair }
|
||
{ Returns a pair of random primes for use in RSA key generation. }
|
||
procedure RSAKeyRandomPrimePair(const Bits: Integer; var P, Q: HugeWord;
|
||
const Callback: TRSAGenerateCallback;
|
||
const CallbackData: NativeUInt);
|
||
begin
|
||
RSAKeyRandomPrime1(Bits, P, Callback, CallbackData);
|
||
RSAKeyRandomPrime2(Bits, P, Q, Callback, CallbackData);
|
||
end;
|
||
|
||
{ RSA Generate Keys }
|
||
{ Returns a randomly generated PrivateKey/PublicKey pair. }
|
||
const
|
||
RSAExpCount = 7;
|
||
RSAExp : array[0..RSAExpCount - 1] of Integer = (3, 5, 7, 11, 17, 257, 65537);
|
||
|
||
procedure RSAGenerateKeys(const KeyBits: Integer;
|
||
var PrivateKey: TRSAPrivateKey;
|
||
var PublicKey: TRSAPublicKey;
|
||
const Callback: TRSAGenerateCallback;
|
||
const CallbackData: NativeUInt);
|
||
var
|
||
Abort : Boolean;
|
||
Bits : Integer;
|
||
P, Q, N, E, D, G : HugeWord;
|
||
F, T : Word32;
|
||
R : Boolean;
|
||
begin
|
||
if (KeyBits <= 0) or
|
||
(KeyBits mod HugeWordElementBits <> 0) then
|
||
raise ERSA.Create(SRSAInvalidKeySize);
|
||
Abort := False;
|
||
HugeWordInit(P);
|
||
HugeWordInit(Q);
|
||
HugeWordInit(N);
|
||
HugeWordInit(E);
|
||
HugeWordInit(D);
|
||
HugeWordInit(G);
|
||
try
|
||
Bits := KeyBits div 2;
|
||
repeat
|
||
R := False;
|
||
repeat
|
||
// generate random prime values for p and q
|
||
RSAKeyRandomPrimePair(Bits, P, Q, Callback, CallbackData);
|
||
// calculate n = p * q
|
||
HugeWordMultiply(N, P, Q);
|
||
Assert(HugeWordGetBitCount(N) = KeyBits);
|
||
// save private key primes
|
||
HugeWordAssign(PrivateKey.Prime1, P);
|
||
HugeWordAssign(PrivateKey.Prime2, Q);
|
||
// calculate phi = (p-1) * (q-1)
|
||
HugeWordDec(P);
|
||
HugeWordDec(Q);
|
||
HugeWordMultiply(PrivateKey.Phi, P, Q);
|
||
// choose e such that 1 < e < phi and gcd(e, phi) = 1
|
||
// try 3 values for e before giving up
|
||
T := 0;
|
||
repeat
|
||
if Assigned(Callback) then
|
||
begin
|
||
Callback(CallbackData, Abort);
|
||
if Abort then
|
||
raise ERSAGenerateAborted.Create('Abort');
|
||
end;
|
||
Inc(T);
|
||
F := RSAExp[RandomUniform(RSAExpCount)];
|
||
HugeWordAssignWord32(E, F);
|
||
HugeWordGCD(E, PrivateKey.Phi, G);
|
||
if HugeWordIsOne(G) then
|
||
R := True;
|
||
until R or (T = 3);
|
||
until R;
|
||
// d = inverse(e) mod phi
|
||
until HugeWordModInv(E, PrivateKey.Phi, D);
|
||
// populate PrivateKey and PublicKey
|
||
PrivateKey.KeyBits := KeyBits;
|
||
HugeWordMod(D, P, PrivateKey.Exponent1); // d mod (p - 1)
|
||
HugeWordMod(D, Q, PrivateKey.Exponent2); // d mod (q - 1)
|
||
HugeWordAssign(PrivateKey.Modulus, N);
|
||
HugeWordAssign(PrivateKey.Exponent, D);
|
||
HugeWordAssign(PrivateKey.PublicExponent, E);
|
||
PublicKey.KeyBits := KeyBits;
|
||
HugeWordAssign(PublicKey.Modulus, N);
|
||
HugeWordAssign(PublicKey.Exponent, E);
|
||
finally
|
||
SecureHugeWordFinalise(G);
|
||
SecureHugeWordFinalise(D);
|
||
SecureHugeWordFinalise(E);
|
||
SecureHugeWordFinalise(N);
|
||
SecureHugeWordFinalise(Q);
|
||
SecureHugeWordFinalise(P);
|
||
end;
|
||
end;
|
||
|
||
{ RSA Cipher Message Buf Size }
|
||
function RSACipherMessageBufSize(const KeySize: Integer): Integer;
|
||
begin
|
||
Result := KeySize div 8;
|
||
end;
|
||
|
||
{ RSA Encode Message PKCS1 }
|
||
{ Encodes a message buffer as a RSA message. }
|
||
{ Uses EME-PKCS1-v1_5 encoding. }
|
||
{ EM = 0x00 || 0x02 || PS || 0x00 || M }
|
||
procedure RSAEncodeMessageEME_PKCS1(
|
||
const KeyBits: Int32;
|
||
const Buf; const BufSize: Integer;
|
||
var EncodedMessage: TRSAMessage);
|
||
var
|
||
N, L, I, C : Integer;
|
||
P, Q : PByte;
|
||
begin
|
||
// validate
|
||
if (KeyBits <= 0) or
|
||
(KeyBits mod HugeWordElementBits <> 0) then
|
||
raise ERSA.Create(SRSAInvalidKeySize);
|
||
// message size
|
||
N := KeyBits div 8; // number of bytes in key (max message size)
|
||
C := BufSize;
|
||
if C < 0 then
|
||
C := 0;
|
||
L := N - 3 - C; // number of padding bytes in PS
|
||
if L < 8 then
|
||
raise ERSA.Create(SRSAMessageTooLong);
|
||
HugeWordSetSize(EncodedMessage, N div HugeWordElementSize);
|
||
// 0x00
|
||
P := EncodedMessage.Data;
|
||
Inc(P, N - 1);
|
||
P^ := 0;
|
||
// 0x02
|
||
Dec(P);
|
||
P^ := 2;
|
||
// PS
|
||
Dec(P);
|
||
for I := 0 to L - 1 do
|
||
begin
|
||
P^ := RandomByteNonZero;
|
||
Dec(P);
|
||
end;
|
||
// 0x00
|
||
P^ := 0;
|
||
Dec(P);
|
||
// M
|
||
if C = 0 then
|
||
exit;
|
||
Q := @Buf;
|
||
for I := 0 to C - 1 do
|
||
begin
|
||
P^ := Q^;
|
||
Dec(P);
|
||
Inc(Q);
|
||
end;
|
||
end;
|
||
|
||
{ RSA OAEP MGF1 }
|
||
{ Mask generation function (MGF) function for OAEP encoding. }
|
||
{ This implements MGF1 from PKCS1v2-1 using SHA1 hashing. }
|
||
{ }
|
||
{ MGF1 (mgfSeed, maskLen) }
|
||
{ mgfSeed = seed from which mask is generated, an octet string }
|
||
{ maskLen = intended length in octets of the mask, at most 2^32 * hLen }
|
||
{ Hash = hash function }
|
||
{ hLen = length in octets of the hash function output }
|
||
{ mask = mask, an octet string of length maskLen }
|
||
{ Steps: }
|
||
{ 1. If maskLen > 2^32 * hLen, output <20>mask too long<6E> and stop. }
|
||
{ 2. Let T be the empty octet string. }
|
||
{ 3. For counter from 0 to [ maskLen / hLen ] <20> 1, do the following: }
|
||
{ a. Convert counter to an octet string C of length 4 octets }
|
||
{ C = I2OSP (counter, 4) }
|
||
{ b. Concatenate the hash of the seed mgfSeed and C to the octet string T }
|
||
{ T = T || Hash (mgfSeed || C) }
|
||
{ 4. Output the leading maskLen octets of T as the octet string mask. }
|
||
procedure RSAOAEPMGF1(
|
||
const SeedBuf; const SeedBufSize: Integer;
|
||
var MaskBuf; const MaskBufSize: Integer);
|
||
var
|
||
N, I, C, D, J : Integer;
|
||
HashStr : RawByteString;
|
||
HashSHA1 : T160BitDigest;
|
||
P, Q, R : PByte;
|
||
const
|
||
hLen = SizeOf(T160BitDigest);
|
||
begin
|
||
Assert(SeedBufSize > 0);
|
||
Assert(MaskBufSize > 0);
|
||
|
||
SetLength(HashStr, SeedBufSize + 4);
|
||
N := (MaskBufSize + hLen - 1) div hLen;
|
||
C := MaskBufSize;
|
||
P := @MaskBuf;
|
||
for I := 0 to N - 1 do
|
||
begin
|
||
// HashStr = mgfSeed || C
|
||
Move(SeedBuf, HashStr[1], SeedBufSize);
|
||
R := @HashStr[SeedBufSize + 1];
|
||
Q := @I;
|
||
Inc(Q, 3);
|
||
for J := 0 to 3 do
|
||
begin
|
||
R^ := Q^;
|
||
Inc(R);
|
||
Dec(Q);
|
||
end;
|
||
// HashSHA1 = Hash (mgfSeed || C)
|
||
HashSHA1 := CalcSHA1(HashStr);
|
||
// T = T || Hash (mgfSeed || C)
|
||
D := C;
|
||
if D > hLen then
|
||
D := hLen;
|
||
Move(HashSHA1, P^, D);
|
||
Inc(P, D);
|
||
Dec(C, D);
|
||
end;
|
||
end;
|
||
|
||
{ RSA Decode Message PKCS1 }
|
||
{ Decodes message previously encoded with RSAEncodeMessagePKCS1. }
|
||
{ Uses EME-PKCS1-v1_5 encoding. }
|
||
{ EM = 0x00 || 0x02 || PS || 0x00 || M }
|
||
{ Returns number of bytes needed to decode message. }
|
||
function RSADecodeMessageEME_PKCS1(
|
||
const KeyBits: Int32;
|
||
const EncodedMessage: HugeWord;
|
||
var Buf; const BufSize: Integer): Integer;
|
||
var
|
||
L, I : Integer;
|
||
P, Q : PByte;
|
||
begin
|
||
// validate
|
||
if (KeyBits <= 0) or
|
||
(KeyBits mod HugeWordElementBits <> 0) then
|
||
raise ERSA.Create(SRSAInvalidKeySize);
|
||
if HugeWordGetBitCount(EncodedMessage) <> KeyBits then
|
||
raise ERSA.Create(SRSAInvalidMessage);
|
||
// decode
|
||
L := HugeWordGetSize(EncodedMessage) * HugeWordElementSize;
|
||
if L < 3 then
|
||
raise ERSA.Create(SRSAInvalidMessage);
|
||
// 0x00
|
||
P := EncodedMessage.Data;
|
||
Inc(P, L - 1);
|
||
if P^ <> 0 then
|
||
raise ERSA.Create(SRSAInvalidMessage);
|
||
// 0x02
|
||
Dec(P);
|
||
if P^ <> 2 then
|
||
raise ERSA.Create(SRSAInvalidMessage);
|
||
Dec(L, 2);
|
||
// PS
|
||
if L < 9 then
|
||
raise ERSA.Create(SRSAInvalidMessage);
|
||
repeat
|
||
Dec(P);
|
||
Dec(L);
|
||
until (L = 0) or (P^ = 0);
|
||
// 0x00
|
||
if P^ <> 0 then
|
||
raise ERSA.Create(SRSAInvalidMessage);
|
||
// M
|
||
Result := L;
|
||
if L = 0 then
|
||
exit;
|
||
if BufSize = 0 then
|
||
exit;
|
||
Dec(P);
|
||
Q := @Buf;
|
||
for I := 0 to L - 1 do
|
||
begin
|
||
Q^ := P^;
|
||
if I >= BufSize then
|
||
exit;
|
||
Dec(P);
|
||
Inc(Q);
|
||
end;
|
||
end;
|
||
|
||
{ RSA XOR Buf }
|
||
procedure RSAXORBuf(
|
||
var Buf; const BufSize: Integer;
|
||
const MaskBuf; const MaskSize: Integer);
|
||
var
|
||
N, I, J, C : Integer;
|
||
P, Q : PByte;
|
||
begin
|
||
Assert(MaskSize > 0);
|
||
|
||
C := BufSize;
|
||
if C < 0 then
|
||
C := 0;
|
||
if C = 0 then
|
||
exit;
|
||
N := (C + MaskSize - 1) div MaskSize;
|
||
P := @Buf;
|
||
for I := 0 to N - 1 do
|
||
begin
|
||
Q := @MaskBuf;
|
||
for J := 0 to MaskSize - 1 do
|
||
begin
|
||
P^ := P^ xor Q^;
|
||
Inc(P);
|
||
Inc(Q);
|
||
Dec(C);
|
||
if C = 0 then
|
||
exit;
|
||
end;
|
||
end;
|
||
end;
|
||
|
||
{ RSA Encode Message OAEP }
|
||
{ Encodes a message buffer as a RSA message. }
|
||
{ Uses EME-OAEP encoding using SHA1 hashing. }
|
||
{ }
|
||
{ EME-OAEP-Encode(M, P,emLen) }
|
||
{ M = message to be encoded, length at most emLen - 2 - 2 * hLen }
|
||
{ mLen = length in octets of the message M }
|
||
{ hLen = length in octets of the hash function output }
|
||
{ PS = emLen - mLen - 2 * hLen - 2 zero octets }
|
||
{ P = encoding parameters, an octet string (default empty) }
|
||
{ pHash = Hash(P), an octet string of length hLen }
|
||
{ DB = pHash || PS || 01 || M }
|
||
{ seed = random octet string of length hLen }
|
||
{ dbMask = MGF(seed, emLen - hLen) }
|
||
{ maskedDB = DB x dbMask }
|
||
{ seedMask = MGF(maskedDB, hLen) }
|
||
{ maskedSeed = seed x seedMask }
|
||
{ EM = 0x00 || maskedSeed || maskedDB }
|
||
const
|
||
RSAOAEPHashBufSize = SizeOf(T160BitDigest);
|
||
|
||
{.DEFINE DEBUG_RSAFixedSeed}
|
||
procedure RSAEncodeMessageEME_OAEP(
|
||
const KeyBits: Int32;
|
||
const Buf; const BufSize: Integer;
|
||
var EncodedMessage: TRSAMessage);
|
||
var
|
||
mLen, emLen, psLen, dbMaskLen, dbLen, I : Integer;
|
||
seed, PS, dbMask, pHash, DB, maskedDB, seedMask, maskedSeed, EM : RawByteString;
|
||
P, Q : PByte;
|
||
const
|
||
hLen = RSAOAEPHashBufSize;
|
||
begin
|
||
// validate
|
||
if (KeyBits <= 0) or
|
||
(KeyBits mod HugeWordElementBits <> 0) then
|
||
raise ERSA.Create(SRSAInvalidKeySize);
|
||
// message size
|
||
emLen := KeyBits div 8; // number of bytes in key (max message size)
|
||
mLen := BufSize;
|
||
if mLen < 0 then
|
||
mLen := 0;
|
||
if mLen > emLen - 2 * hLen - 2 then
|
||
raise ERSA.Create(SRSAMessageTooLong);
|
||
HugeWordSetSize(EncodedMessage, emLen div HugeWordElementSize);
|
||
// pHash = Hash(P), an octet string of length hLen
|
||
// SetLength(pHash, hLen);
|
||
// HashP := CalcSHA1('');
|
||
// Move(HashP, pHash[1], hLen);
|
||
pHash := RawByteString(
|
||
#$DA#$39#$A3#$EE#$5E#$6B#$4B#$0D#$32#$55 +
|
||
#$BF#$EF#$95#$60#$18#$90#$AF#$D8#$07#$09); ////
|
||
// seed = random octet string of length hLen
|
||
{$IFDEF DEBUG_RSAFixedSeed}
|
||
seed := RawByteString(
|
||
#$aa#$fd#$12#$f6#$59#$ca#$e6#$34#$89#$b4 +
|
||
#$79#$e5#$07#$6d#$de#$c2#$f0#$6c#$b5#$8f);
|
||
{$ELSE}
|
||
SetLength(seed, hLen);
|
||
for I := 1 to hLen do
|
||
seed[I] := AnsiChar(RandomByteNonZero);
|
||
{$ENDIF}
|
||
// PS = emLen - mLen - 2 * hLen - 2 zero octets
|
||
psLen := emLen - mLen - 2 * hLen - 2;
|
||
SetLength(PS, psLen);
|
||
for I := 1 to psLen do
|
||
PS[I] := #0;
|
||
// dbMask = MGF(seed, emLen - hLen - 1)
|
||
dbMaskLen := emLen - hLen - 1;
|
||
SetLength(dbMask, dbMaskLen);
|
||
RSAOAEPMGF1(seed[1], hLen, dbMask[1], dbMaskLen);
|
||
// DB = pHash || PS || 01 || M
|
||
dbLen := hLen + psLen + 1 + mLen;
|
||
SetLength(DB, dbLen);
|
||
P := @DB[1];
|
||
Move(pHash[1], P^, hLen);
|
||
Inc(P, hLen);
|
||
Move(PS[1], P^, psLen);
|
||
Inc(P, psLen);
|
||
P^ := 1;
|
||
Inc(P);
|
||
Move(Buf, P^, mLen);
|
||
// maskedDB = DB x dbMask
|
||
SetLength(maskedDB, dbLen);
|
||
Move(DB[1], maskedDB[1], dbLen);
|
||
RSAXORBuf(maskedDB[1], dbLen, dbMask[1], dbMaskLen);
|
||
// seedMask = MGF(maskedDB, hLen)
|
||
SetLength(seedMask, hLen);
|
||
RSAOAEPMGF1(maskedDB[1], dbLen, seedMask[1], hLen);
|
||
// maskedSeed = seed x seedMask
|
||
SetLength(maskedSeed, hLen);
|
||
Move(seed[1], maskedSeed[1], hLen);
|
||
RSAXORBuf(maskedSeed[1], hLen, seedMask[1], hLen);
|
||
// EM = 0x00 || maskedSeed || maskedDB
|
||
SetLength(EM, emLen);
|
||
P := @EM[1];
|
||
P^ := 0;
|
||
Inc(P);
|
||
Move(maskedSeed[1], P^, hLen);
|
||
Inc(P, hLen);
|
||
Move(maskedDB[1], P^, dbLen);
|
||
// populate message
|
||
P := EncodedMessage.Data;
|
||
Inc(P, emLen - 1);
|
||
Q := @EM[1];
|
||
for I := 0 to emLen - 1 do
|
||
begin
|
||
P^ := Q^;
|
||
Dec(P);
|
||
Inc(Q);
|
||
end;
|
||
end;
|
||
|
||
{ RSA Decode Message OAEP }
|
||
{ Decodes message previously encoded with RSAEncodeMessageOAEP. }
|
||
{ Uses EME-OAEP encoding using SHA1 hashing. }
|
||
{ }
|
||
{ EME-OAEP-Encode(M, P,emLen) }
|
||
{ M = message to be encoded, length at most emLen - 2 - 2h * Len }
|
||
{ mLen = length in octets of the message M }
|
||
{ hLen = length in octets of the hash function output }
|
||
{ PS = emLen - mLen - 2 * hLen - 2 zero octets }
|
||
{ P = encoding parameters, an octet string (default empty) }
|
||
{ pHash = Hash(P), an octet string of length hLen }
|
||
{ DB = pHash || PS || 01 || M }
|
||
{ seed = random octet string of length hLen }
|
||
{ dbMask = MGF(seed, emLen - hLen) }
|
||
{ maskedDB = DB x dbMask }
|
||
{ seedMask = MGF(maskedDB, hLen) }
|
||
{ maskedSeed = seed x seedMask }
|
||
{ EM = 0x00 || maskedSeed || maskedDB }
|
||
function RSADecodeMessageEME_OAEP(
|
||
const KeyBits: Int32;
|
||
const EncodedMessage: HugeWord;
|
||
var Buf; const BufSize: Integer): Integer;
|
||
var
|
||
I, L, emLen, dbLen : Integer;
|
||
maskedSeed, maskedDB, seedMask, seed, dbMask, DB, pHash, pHashPr : RawByteString;
|
||
P, Q : PByte;
|
||
const
|
||
hLen = RSAOAEPHashBufSize;
|
||
begin
|
||
// validate
|
||
if (KeyBits <= 0) or
|
||
(KeyBits mod HugeWordElementBits <> 0) then
|
||
raise ERSA.Create(SRSAInvalidKeySize);
|
||
if HugeWordGetBitCount(EncodedMessage) <> KeyBits then
|
||
raise ERSA.Create(SRSAInvalidMessage);
|
||
// decode
|
||
emLen := HugeWordGetSize(EncodedMessage) * HugeWordElementSize;
|
||
// EM = 0x00 || maskedSeed || maskedDB
|
||
dbLen := emLen - hLen - 1;
|
||
SetLength(maskedSeed, hLen);
|
||
SetLength(maskedDB, dbLen);
|
||
P := EncodedMessage.Data;
|
||
Inc(P, emLen - 1);
|
||
if P^ <> 0 then
|
||
raise ERSA.Create(SRSAInvalidMessage);
|
||
Dec(P);
|
||
Q := @maskedSeed[1];
|
||
for I := 0 to hLen - 1 do
|
||
begin
|
||
Q^ := P^;
|
||
Dec(P);
|
||
Inc(Q);
|
||
end;
|
||
Q := @maskedDB[1];
|
||
for I := 0 to dbLen - 1 do
|
||
begin
|
||
Q^ := P^;
|
||
Dec(P);
|
||
Inc(Q);
|
||
end;
|
||
// Let seedMask = MGF(maskedDB, hLen)
|
||
SetLength(seedMask, hLen);
|
||
RSAOAEPMGF1(maskedDB[1], dbLen, seedMask[1], hLen);
|
||
// Let seed = maskedSeed xor seedMask
|
||
SetLength(seed, hLen);
|
||
Move(maskedSeed[1], seed[1], hLen);
|
||
RSAXORBuf(seed[1], hLen, seedMask[1], hLen);
|
||
// Let dbMask = MGF(seed, ||EM|| - hLen)
|
||
SetLength(dbMask, dbLen);
|
||
RSAOAEPMGF1(seed[1], hLen, dbMask[1], dbLen);
|
||
// Let DB = maskedDB xor dbMask.
|
||
SetLength(DB, dbLen);
|
||
Move(maskedDB[1], DB[1], dbLen);
|
||
RSAXORBuf(DB[1], dbLen, dbMask[1], dbLen);
|
||
// Let pHash = Hash(P), an octet string of length hLen
|
||
// SetLength(pHash, hLen);
|
||
// Hash := CalcSHA1('');
|
||
// Move(Hash, pHash[1], hLen);
|
||
pHash := RawByteString(
|
||
#$DA#$39#$A3#$EE#$5E#$6B#$4B#$0D#$32#$55 +
|
||
#$BF#$EF#$95#$60#$18#$90#$AF#$D8#$07#$09);
|
||
// DB = pHash' || PS || 01 || M
|
||
// Decode pHash'
|
||
SetLength(pHashPr, hLen);
|
||
Move(DB[1], pHashPr[1], hLen);
|
||
if pHashPr <> pHash then
|
||
raise ERSA.Create(SRSAInvalidMessage);
|
||
// Decode PS || 01
|
||
I := hLen + 1;
|
||
while I <= dbLen do
|
||
case Byte(DB[I]) of
|
||
0 : Inc(I);
|
||
1 : break;
|
||
else
|
||
raise ERSA.Create(SRSAInvalidMessage);
|
||
end;
|
||
if I > dbLen then
|
||
raise ERSA.Create(SRSAInvalidMessage);
|
||
if Byte(DB[I]) <> 1 then
|
||
raise ERSA.Create(SRSAInvalidMessage);
|
||
Inc(I);
|
||
// Decode M
|
||
L := dbLen - I + 1;
|
||
Result := L;
|
||
if L > BufSize then
|
||
L := BufSize;
|
||
if L > 0 then
|
||
Move(DB[I], Buf, L);
|
||
end;
|
||
|
||
{ RSA Cipher Message To Buf }
|
||
{ Copies cipher message to buffer. }
|
||
{ Returns the buffer size required for the message. }
|
||
function RSACipherMessageToBuf(
|
||
const KeyBits: Int32;
|
||
const CipherMessage: TRSAMessage;
|
||
var CipherBuf; const CipherBufSize: Integer): Integer;
|
||
var
|
||
L, I : Integer;
|
||
P, Q : PByte;
|
||
begin
|
||
if HugeWordGetBitCount(CipherMessage) <> KeyBits then
|
||
raise ERSA.Create(SRSAInvalidMessage);
|
||
L := KeyBits div 8;
|
||
Result := L;
|
||
if CipherBufSize <= 0 then
|
||
exit;
|
||
P := CipherMessage.Data;
|
||
Inc(P, L - 1);
|
||
Q := @CipherBuf;
|
||
for I := 0 to L - 1 do
|
||
begin
|
||
if I >= CipherBufSize then
|
||
exit;
|
||
Q^ := P^;
|
||
Inc(Q);
|
||
Dec(P);
|
||
end;
|
||
end;
|
||
|
||
{ RSA Cipher Buf To Message }
|
||
procedure RSACipherBufToMessage(
|
||
const KeyBits: Int32;
|
||
const CipherBuf; const CipherBufSize: Integer;
|
||
var CipherMessage: TRSAMessage);
|
||
var
|
||
L, I : Integer;
|
||
P, Q : PByte;
|
||
begin
|
||
// validate
|
||
if (KeyBits <= 0) or
|
||
(KeyBits mod HugeWordElementBits <> 0) then
|
||
raise ERSA.Create(SRSAInvalidKeySize);
|
||
// message size
|
||
L := KeyBits div 8;
|
||
if CipherBufSize <> L then
|
||
raise ERSA.Create(SRSAInvalidBufferSize);
|
||
HugeWordSetSize(CipherMessage, L div HugeWordElementSize);
|
||
// move data
|
||
P := CipherMessage.Data;
|
||
Inc(P, L - 1);
|
||
Q := @CipherBuf;
|
||
for I := 0 to L - 1 do
|
||
begin
|
||
P^ := Q^;
|
||
Dec(P);
|
||
Inc(Q);
|
||
end;
|
||
end;
|
||
|
||
{ RSA Encrypt Message }
|
||
procedure RSAEncryptMessage(
|
||
const PublicKey: TRSAPublicKey;
|
||
const PlainMessage: TRSAMessage;
|
||
var CipherMessage: TRSAMessage);
|
||
begin
|
||
// validate
|
||
if (PublicKey.KeyBits <= 0) or
|
||
(PublicKey.KeyBits mod HugeWordElementBits <> 0) then
|
||
raise ERSA.Create(SRSAInvalidKeySize);
|
||
if HugeWordCompare(PlainMessage, PublicKey.Modulus) >= 0 then
|
||
raise ERSA.Create(SRSAInvalidMessage);
|
||
Assert(HugeWordGetBitCount(PlainMessage) = PublicKey.KeyBits);
|
||
// encrypt
|
||
HugeWordPowerAndMod(CipherMessage, PlainMessage, PublicKey.Exponent, PublicKey.Modulus);
|
||
Assert(HugeWordGetBitCount(CipherMessage) = PublicKey.KeyBits);
|
||
end;
|
||
|
||
{ RSA Encrypt }
|
||
function RSAEncrypt(
|
||
const EncryptionType: TRSAEncryptionType;
|
||
const PublicKey: TRSAPublicKey;
|
||
const PlainBuf; const PlainBufSize: Integer;
|
||
var CipherBuf; const CipherBufSize: Integer): Integer;
|
||
var
|
||
EncodedMsg, CipherMsg : HugeWord;
|
||
begin
|
||
// validate
|
||
if (PublicKey.KeyBits <= 0) or
|
||
(PublicKey.KeyBits mod HugeWordElementBits <> 0) then
|
||
raise ERSA.Create(SRSAInvalidKeySize);
|
||
if (PlainBufSize < 0) or
|
||
(CipherBufSize <= 0) then
|
||
raise ERSA.Create(SRSAInvalidBufferSize);
|
||
// encrypt
|
||
HugeWordInit(EncodedMsg);
|
||
HugeWordInit(CipherMsg);
|
||
try
|
||
case EncryptionType of
|
||
rsaetRSAES_PKCS1 : RSAEncodeMessageEME_PKCS1(PublicKey.KeyBits, PlainBuf, PlainBufSize, EncodedMsg);
|
||
rsaetRSAES_OAEP : RSAEncodeMessageEME_OAEP(PublicKey.KeyBits, PlainBuf, PlainBufSize, EncodedMsg);
|
||
else
|
||
raise ERSA.Create(SRSAInvalidEncryptionType);
|
||
end;
|
||
RSAEncryptMessage(PublicKey, EncodedMsg, CipherMsg);
|
||
Result := RSACipherMessageToBuf(PublicKey.KeyBits, CipherMsg, CipherBuf, CipherBufSize);
|
||
if Result > CipherBufSize then
|
||
raise ERSA.Create(SRSAInvalidBufferSize);
|
||
finally
|
||
SecureHugeWordFinalise(CipherMsg);
|
||
SecureHugeWordFinalise(EncodedMsg);
|
||
end;
|
||
end;
|
||
|
||
{ RSA Encrypt Str }
|
||
function RSAEncryptStr(
|
||
const EncryptionType: TRSAEncryptionType;
|
||
const PublicKey: TRSAPublicKey;
|
||
const Plain: RawByteString): RawByteString;
|
||
var
|
||
L : Integer;
|
||
begin
|
||
L := RSACipherMessageBufSize(PublicKey.KeyBits);
|
||
SetLength(Result, L);
|
||
L := RSAEncrypt(EncryptionType, PublicKey, PByteChar(Plain)^, Length(Plain),
|
||
PByteChar(Result)^, L);
|
||
SetLength(Result, L);
|
||
end;
|
||
|
||
{ RSA Decrypt Message }
|
||
{ Decrypts using m = c^d mod n }
|
||
procedure RSADecryptMessage(
|
||
const PrivateKey: TRSAPrivateKey;
|
||
const CipherMessage: TRSAMessage;
|
||
var EncodedMessage: TRSAMessage);
|
||
begin
|
||
// validate
|
||
if (PrivateKey.KeyBits <= 0) or
|
||
(PrivateKey.KeyBits mod HugeWordElementBits <> 0) then
|
||
raise ERSA.Create(SRSAInvalidKeySize);
|
||
if HugeWordGetBitCount(CipherMessage) <> PrivateKey.KeyBits then
|
||
raise ERSA.Create(SRSAInvalidMessage);
|
||
// decrypt
|
||
HugeWordPowerAndMod(EncodedMessage, CipherMessage, PrivateKey.Exponent, PrivateKey.Modulus);
|
||
end;
|
||
|
||
{
|
||
Alternative decryption algorithm (works with smaller factors):
|
||
if the second form (p, q, dP, dQ,qInv) of K is used:
|
||
Let m1 = c^dP mod p
|
||
Let m2 = c^dQ mod q
|
||
Let h = (m1 - m2) . qInv mod p
|
||
Let m = m2 + q . h
|
||
}
|
||
procedure RSADecryptMessage2(
|
||
const PrivateKey: TRSAPrivateKey;
|
||
const CipherMessage: TRSAMessage;
|
||
var EncodedMessage: TRSAMessage);
|
||
var
|
||
A, M1, M2, H, M : HugeWord;
|
||
begin
|
||
HugeWordInit(A);
|
||
HugeWordInit(M1);
|
||
HugeWordInit(M2);
|
||
HugeWordInit(H);
|
||
try
|
||
// m1 = c^dP mod p
|
||
HugeWordPowerAndMod(M1, CipherMessage, PrivateKey.Exponent1, PrivateKey.Prime1);
|
||
// m2 = c^dQ mod q
|
||
HugeWordPowerAndMod(M2, CipherMessage, PrivateKey.Exponent2, PrivateKey.Prime2);
|
||
// h = (m1 - m2) . qInv mod p
|
||
HugeWordAssign(H, M1);
|
||
HugeWordSubtract(H, M2);
|
||
HugeWordMultiply(H, H, PrivateKey.Coefficient);
|
||
// m = m2 + q . h
|
||
HugeWordMultiply(M, PrivateKey.Prime2, H);
|
||
HugeWordAdd(M, M2);
|
||
finally
|
||
SecureHugeWordFinalise(H);
|
||
SecureHugeWordFinalise(M2);
|
||
SecureHugeWordFinalise(M1);
|
||
SecureHugeWordFinalise(A);
|
||
end;
|
||
end;
|
||
|
||
{ RSA Decrypt }
|
||
function RSADecrypt(
|
||
const EncryptionType: TRSAEncryptionType;
|
||
const PrivateKey: TRSAPrivateKey;
|
||
const CipherBuf; const CipherBufSize: Integer;
|
||
var PlainBuf; const PlainBufSize: Integer): Integer;
|
||
var
|
||
CipherMsg, EncodedMsg : HugeWord;
|
||
begin
|
||
// validate
|
||
if (PrivateKey.KeyBits <= 0) or
|
||
(PrivateKey.KeyBits mod HugeWordElementBits <> 0) then
|
||
raise ERSA.Create(SRSAInvalidKeySize);
|
||
// decrypt
|
||
HugeWordInit(CipherMsg);
|
||
HugeWordInit(EncodedMsg);
|
||
try
|
||
RSACipherBufToMessage(PrivateKey.KeyBits, CipherBuf, CipherBufSize, CipherMsg);
|
||
RSADecryptMessage(PrivateKey, CipherMsg, EncodedMsg);
|
||
case EncryptionType of
|
||
rsaetRSAES_PKCS1 : Result := RSADecodeMessageEME_PKCS1(PrivateKey.KeyBits, EncodedMsg, PlainBuf, PlainBufSize);
|
||
rsaetRSAES_OAEP : Result := RSADecodeMessageEME_OAEP(PrivateKey.KeyBits, EncodedMsg, PlainBuf, PlainBufSize);
|
||
else
|
||
raise ERSA.Create(SRSAInvalidEncryptionType);
|
||
end;
|
||
finally
|
||
SecureHugeWordFinalise(EncodedMsg);
|
||
SecureHugeWordFinalise(CipherMsg);
|
||
end;
|
||
end;
|
||
|
||
{ RSA Decrypt Str }
|
||
function RSADecryptStr(
|
||
const EncryptionType: TRSAEncryptionType;
|
||
const PrivateKey: TRSAPrivateKey;
|
||
const Cipher: RawByteString): RawByteString;
|
||
var
|
||
L, N : Integer;
|
||
begin
|
||
L := Length(Cipher);
|
||
if L = 0 then
|
||
raise ERSA.Create(SRSAInvalidMessage);
|
||
N := RSACipherMessageBufSize(PrivateKey.KeyBits);
|
||
SetLength(Result, N);
|
||
N := RSADecrypt(EncryptionType, PrivateKey, PByteChar(Cipher)^, L, PByteChar(Result)^, N);
|
||
SetLength(Result, N);
|
||
end;
|
||
|
||
|
||
|
||
{ EMSA_PKCS1 }
|
||
|
||
function GetRSAHashTypeDigestInfo(const HashType: TRSAHashType): TBytes;
|
||
begin
|
||
case HashType of
|
||
rsahfMD5 : Result := TBytes.Create($30, $20, $30, $0c, $06, $08, $2a, $86, $48, $86, $f7, $0d, $02, $05, $05, $00, $04, $10);
|
||
rsahfSHA1 : Result := TBytes.Create($30, $21, $30, $09, $06, $05, $2b, $0e, $03, $02, $1a, $05, $00, $04, $14);
|
||
rsahfSHA256 : Result := TBytes.Create($30, $31, $30, $0d, $06, $09, $60, $86, $48, $01, $65, $03, $04, $02, $01, $05, $00, $04, $20);
|
||
rsahfSHA384 : Result := TBytes.Create($30, $41, $30, $0d, $06, $09, $60, $86, $48, $01, $65, $03, $04, $02, $02, $05, $00, $04, $30);
|
||
rsahfSHA512 : Result := TBytes.Create($30, $51, $30, $0d, $06, $09, $60, $86, $48, $01, $65, $03, $04, $02, $03, $05, $00, $04, $40);
|
||
else
|
||
Result := nil;
|
||
end;
|
||
end;
|
||
|
||
const
|
||
RSAHashTypeDigestSize : array[TRSAHashType] of Int32 = (
|
||
16,
|
||
20,
|
||
32,
|
||
48,
|
||
64
|
||
);
|
||
|
||
function RSAHashBuf(
|
||
const HashType: TRSAHashType;
|
||
const Buf; const BufSize: NativeInt;
|
||
const Digest; const DigestSize: Integer): Integer;
|
||
var
|
||
DigSize : Int32;
|
||
begin
|
||
DigSize := RSAHashTypeDigestSize[HashType];
|
||
if DigestSize < DigSize then
|
||
raise ERSA.Create('Invalid digest buffer size');
|
||
case HashType of
|
||
rsahfMD5 : P128BitDigest(@Digest)^ := CalcMD5(Buf, BufSize);
|
||
rsahfSHA1 : P160BitDigest(@Digest)^ := CalcSHA1(Buf, BufSize);
|
||
rsahfSHA256 : P256BitDigest(@Digest)^ := CalcSHA256(Buf, BufSize);
|
||
rsahfSHA384 : P384BitDigest(@Digest)^ := CalcSHA384(Buf, BufSize);
|
||
rsahfSHA512 : P512BitDigest(@Digest)^ := CalcSHA512(Buf, BufSize);
|
||
else
|
||
raise ERSA.Create('Invalid hash type');
|
||
end;
|
||
Result := DigSize;
|
||
end;
|
||
|
||
procedure RSAEncodeMessageEMSA_PKCS1(
|
||
const KeyBits: Int32;
|
||
const HashType: TRSAHashType;
|
||
const Buf; const BufSize: NativeInt;
|
||
var EncodedMessage: TRSAMessage);
|
||
var
|
||
KeySize : Integer;
|
||
H : T512BitDigest;
|
||
T : TBytes;
|
||
InfoSize : Integer;
|
||
TLen : Integer;
|
||
HshSize : Integer;
|
||
P : PByte;
|
||
I : Integer;
|
||
N : Integer;
|
||
begin
|
||
// validate
|
||
if (KeyBits <= 0) or
|
||
(KeyBits mod HugeWordElementBits <> 0) then
|
||
raise ERSA.Create(SRSAInvalidKeySize);
|
||
// hash: H = Hash(M)
|
||
HshSize := RSAHashBuf(HashType, Buf, BufSize, H, SizeOf(H));
|
||
// DigestInfo ::= SEQUENCE { digestAlgorithm AlgorithmIdentifier, digest OCTET STRING }
|
||
T := GetRSAHashTypeDigestInfo(HashType);
|
||
InfoSize := Length(T);
|
||
TLen := InfoSize + HshSize;
|
||
SetLength(T, TLen);
|
||
Move(H, T[InfoSize], HshSize);
|
||
// EM = 0x00 || 0x01 || PS || 0x00 || T
|
||
KeySize := KeyBits div 8;
|
||
HugeWordSetSize(EncodedMessage, KeyBits div HugeWordElementBits);
|
||
P := EncodedMessage.Data;
|
||
Inc(P, KeySize - 1);
|
||
// 0x00
|
||
P^ := 0;
|
||
Dec(P);
|
||
// 0x01
|
||
P^ := 1;
|
||
Dec(P);
|
||
// PS
|
||
N := KeySize - 3 - TLen;
|
||
if N < 0 then
|
||
raise ERSA.Create('Invalid key size');
|
||
for I := 0 to N - 1 do
|
||
begin
|
||
P^ := $FF;
|
||
Dec(P);
|
||
end;
|
||
// 0x00
|
||
P^ := 0;
|
||
Dec(P);
|
||
// T
|
||
for I := 0 to TLen - 1 do
|
||
begin
|
||
P^ := T[I];
|
||
Dec(P);
|
||
end;
|
||
end;
|
||
|
||
function RSADecodeMessageEMSA_PKCS1(
|
||
const KeyBits: Int32;
|
||
const EncodedMessage: HugeWord;
|
||
var HashType: TRSAHashType;
|
||
var Buf; const BufSize: Integer): Integer;
|
||
var
|
||
KeySize : Integer;
|
||
N : Integer;
|
||
P, Q, R : PByte;
|
||
Hsh : TRSAHashType;
|
||
HshPre : TBytes;
|
||
HshSiz : Integer;
|
||
I, L : Integer;
|
||
Fnd : Boolean;
|
||
begin
|
||
KeySize := KeyBits div 8;
|
||
P := EncodedMessage.Data;
|
||
N := KeySize;
|
||
if EncodedMessage.Used < KeySize div HugeWordElementSize then
|
||
raise ERSA.Create(SRSAInvalidMessage);
|
||
Inc(P, KeySize - 1);
|
||
// EM = 0x00 || 0x01 || PS || 0x00 || T
|
||
if P^ <> 0 then
|
||
raise ERSA.Create(SRSAInvalidMessage);
|
||
Dec(P);
|
||
Dec(N);
|
||
if P^ <> 1 then
|
||
raise ERSA.Create(SRSAInvalidMessage);
|
||
Dec(P);
|
||
Dec(N);
|
||
while (N > 0) and (P^ = $FF) do
|
||
begin
|
||
Dec(P);
|
||
Dec(N);
|
||
end;
|
||
if N = 0 then
|
||
raise ERSA.Create(SRSAInvalidMessage);
|
||
if P^ <> 0 then
|
||
raise ERSA.Create(SRSAInvalidMessage);
|
||
Dec(P);
|
||
Dec(N);
|
||
if N = 0 then
|
||
raise ERSA.Create(SRSAInvalidMessage);
|
||
// Check hash id
|
||
for Hsh := Low(TRSAHashType) to High(TRSAHashType) do
|
||
begin
|
||
HshPre := GetRSAHashTypeDigestInfo(Hsh);
|
||
if N > Length(HshPre) then
|
||
begin
|
||
Q := P;
|
||
Fnd := True;
|
||
for I := 0 to Length(HshPre) - 1 do
|
||
if Q^ <> HshPre[I] then
|
||
begin
|
||
Fnd := False;
|
||
break;
|
||
end
|
||
else
|
||
Dec(Q);
|
||
if Fnd then
|
||
begin
|
||
HashType := Hsh;
|
||
Dec(N, Length(HshPre));
|
||
HshSiz := RSAHashTypeDigestSize[Hsh];
|
||
Dec(N, HshSiz);
|
||
if N < 0 then
|
||
raise ERSA.Create(SRSAInvalidMessage);
|
||
// move hash digest to Buf
|
||
if HshSiz < BufSize then
|
||
L := HshSiz
|
||
else
|
||
L := BufSize;
|
||
R := @Buf;
|
||
for I := 0 to L - 1 do
|
||
begin
|
||
R^ := Q^;
|
||
Inc(R);
|
||
Dec(Q);
|
||
end;
|
||
Result := KeySize - N;
|
||
exit;
|
||
end;
|
||
end;
|
||
end;
|
||
raise ERSA.Create('Invalid hash id');
|
||
end;
|
||
|
||
|
||
|
||
{ RSA Sign Message }
|
||
procedure RSASignBufToMsg(const KeyBits: Integer; var Msg: HugeWord; const Buf; const BufSize: Integer);
|
||
var
|
||
L, I : Integer;
|
||
P, Q : PByte;
|
||
begin
|
||
Assert(KeyBits > 0);
|
||
L := KeyBits div 8;
|
||
if BufSize > L then
|
||
raise ERSA.Create(SRSAInvalidBufferSize);
|
||
HugeWordSetSize(Msg, L div HugeWordElementSize);
|
||
P := Msg.Data;
|
||
FillChar(P^, L, 0);
|
||
Inc(P, L - 1);
|
||
Q := @Buf;
|
||
for I := 0 to BufSize - 1 do
|
||
begin
|
||
P^ := Q^;
|
||
Dec(P);
|
||
Inc(Q);
|
||
end;
|
||
end;
|
||
|
||
function RSASignMsgToBuf(const KeyBits: Integer; var Buf; const BufSize: Integer; const Msg: HugeWord): Integer;
|
||
var
|
||
L, N, I, C : Integer;
|
||
P, Q : PByte;
|
||
begin
|
||
L := KeyBits div 8;
|
||
if BufSize < L then
|
||
raise ERSA.Create(SRSAInvalidBufferSize);
|
||
N := Msg.Used * HugeWordElementSize;
|
||
P := Msg.Data;
|
||
C := MinInt(N, L);
|
||
if C > BufSize then
|
||
raise ERSA.Create(SRSAInvalidBufferSize);
|
||
Inc(P, C - 1); //// was N - 1
|
||
Q := @Buf;
|
||
for I := 0 to C - 1 do
|
||
begin
|
||
Q^ := P^;
|
||
Dec(P);
|
||
Inc(Q);
|
||
end;
|
||
Result := C;
|
||
end;
|
||
|
||
function RSASignMessage(
|
||
const SignType: TRSASignMessageType;
|
||
const HashType: TRSAHashType;
|
||
const PrivateKey: TRSAPrivateKey;
|
||
const MessageBuf; const MessageBufSize: Integer;
|
||
var SignatureBuf; const SignatureBufSize: Integer): Integer;
|
||
var
|
||
CipherMsg, EncodedMsg : HugeWord;
|
||
L : Integer;
|
||
begin
|
||
// validate
|
||
if (PrivateKey.KeyBits <= 0) or
|
||
(PrivateKey.KeyBits mod HugeWordElementBits <> 0) then
|
||
raise ERSA.Create(SRSAInvalidKeySize);
|
||
L := PrivateKey.KeyBits div 8;
|
||
if SignatureBufSize < L then
|
||
raise ERSA.Create(SRSAInvalidBufferSize);
|
||
// sign
|
||
HugeWordInit(CipherMsg);
|
||
HugeWordInit(EncodedMsg);
|
||
try
|
||
case SignType of
|
||
rsastMessage : RSASignBufToMsg(PrivateKey.KeyBits, CipherMsg, MessageBuf, MessageBufSize);
|
||
rsastEMSA_PKCS1 : RSAEncodeMessageEMSA_PKCS1(PrivateKey.KeyBits, HashType, MessageBuf, MessageBufSize, CipherMsg);
|
||
else
|
||
raise ERSA.Create('Invalid message type');
|
||
end;
|
||
HugeWordPowerAndMod(EncodedMsg, CipherMsg, PrivateKey.Exponent, PrivateKey.Modulus);
|
||
RSASignMsgToBuf(PrivateKey.KeyBits, SignatureBuf, SignatureBufSize, EncodedMsg);
|
||
finally
|
||
SecureHugeWordFinalise(EncodedMsg);
|
||
SecureHugeWordFinalise(CipherMsg);
|
||
end;
|
||
Result := L;
|
||
end;
|
||
|
||
function RSACheckSignature(
|
||
const SignType: TRSASignMessageType;
|
||
const PublicKey: TRSAPublicKey;
|
||
const MessageBuf; const MessageBufSize: NativeInt;
|
||
const SignatureBuf; const SignatureBufSize: Integer): Boolean;
|
||
var
|
||
L, C : Integer;
|
||
RMsgBuf : Pointer;
|
||
RMsgBufSize : Integer;
|
||
EncodedMsg, CipherMsg : HugeWord;
|
||
HashType : TRSAHashType;
|
||
Digest : T512BitDigest;
|
||
DigSize : Integer;
|
||
begin
|
||
// validate
|
||
if (PublicKey.KeyBits <= 0) or
|
||
(PublicKey.KeyBits mod HugeWordElementBits <> 0) then
|
||
raise ERSA.Create(SRSAInvalidKeySize);
|
||
L := PublicKey.KeyBits div 8;
|
||
if SignatureBufSize < L then
|
||
raise ERSA.Create(SRSAInvalidBufferSize);
|
||
// check signature
|
||
RMsgBufSize := L;
|
||
GetMem(RMsgBuf, RMsgBufSize);
|
||
try
|
||
HugeWordInit(EncodedMsg);
|
||
HugeWordInit(CipherMsg);
|
||
try
|
||
RSASignBufToMsg(PublicKey.KeyBits, CipherMsg, SignatureBuf, SignatureBufSize);
|
||
HugeWordPowerAndMod(EncodedMsg, CipherMsg, PublicKey.Exponent, PublicKey.Modulus);
|
||
case SignType of
|
||
rsastMessage :
|
||
begin
|
||
C := RSASignMsgToBuf(PublicKey.KeyBits, RMsgBuf^, RMsgBufSize, EncodedMsg);
|
||
Result := EqualMem(RMsgBuf^, MessageBuf, MinInt(C, MessageBufSize));
|
||
end;
|
||
rsastEMSA_PKCS1 :
|
||
begin
|
||
RSADecodeMessageEMSA_PKCS1(PublicKey.KeyBits, EncodedMsg, HashType, RMsgBuf^, RMsgBufSize);
|
||
DigSize := RSAHashBuf(HashType, MessageBuf, MessageBufSize, Digest, SizeOf(Digest));
|
||
Result := EqualMem(Digest, RMsgBuf^, DigSize);
|
||
end;
|
||
else
|
||
raise ERSA.Create('Invalid message type');
|
||
end;
|
||
finally
|
||
SecureHugeWordFinalise(CipherMsg);
|
||
SecureHugeWordFinalise(EncodedMsg);
|
||
end;
|
||
finally
|
||
FreeMem(RMsgBuf);
|
||
end;
|
||
end;
|
||
|
||
|
||
|
||
{ }
|
||
{ Test }
|
||
{ }
|
||
{$IFDEF CIPHER_TEST}
|
||
{$ASSERTIONS ON}
|
||
procedure Test;
|
||
|
||
procedure TestMGF;
|
||
var Seed, Mask : RawByteString;
|
||
begin
|
||
Seed :=
|
||
RawByteString(
|
||
#$aa#$fd#$12#$f6#$59#$ca#$e6#$34#$89#$b4 +
|
||
#$79#$e5#$07#$6d#$de#$c2#$f0#$6c#$b5#$8f);
|
||
SetLength(Mask, 107);
|
||
FillChar(Mask[1], 107, 0);
|
||
RSAOAEPMGF1(Seed[1], 20, Mask[1], 107);
|
||
Assert(Mask =
|
||
#$06#$e1#$de#$b2#$36#$9a#$a5#$a5#$c7#$07#$d8#$2c#$8e#$4e#$93#$24 +
|
||
#$8a#$c7#$83#$de#$e0#$b2#$c0#$46#$26#$f5#$af#$f9#$3e#$dc#$fb#$25 +
|
||
#$c9#$c2#$b3#$ff#$8a#$e1#$0e#$83#$9a#$2d#$db#$4c#$dc#$fe#$4f#$f4 +
|
||
#$77#$28#$b4#$a1#$b7#$c1#$36#$2b#$aa#$d2#$9a#$b4#$8d#$28#$69#$d5 +
|
||
#$02#$41#$21#$43#$58#$11#$59#$1b#$e3#$92#$f9#$82#$fb#$3e#$87#$d0 +
|
||
#$95#$ae#$b4#$04#$48#$db#$97#$2f#$3a#$c1#$4e#$af#$f4#$9c#$8c#$3b +
|
||
#$7c#$fc#$95#$1a#$51#$ec#$d1#$dd#$e6#$12#$64);
|
||
end;
|
||
|
||
procedure TestEncrypt;
|
||
var EM, CM : TRSAMessage;
|
||
Pub : TRSAPublicKey;
|
||
begin
|
||
HugeWordInit(EM);
|
||
HugeWordInit(CM);
|
||
RSAPublicKeyInit(Pub);
|
||
|
||
RSAPublicKeyAssignHex(Pub,
|
||
1024,
|
||
'bbf82f090682ce9c2338ac2b9da871f7' +
|
||
'368d07eed41043a440d6b6f07454f51f' +
|
||
'b8dfbaaf035c02ab61ea48ceeb6fcd48' +
|
||
'76ed520d60e1ec4619719d8a5b8b807f' +
|
||
'afb8e0a3dfc737723ee6b4b7d93a2584' +
|
||
'ee6a649d060953748834b2454598394e' +
|
||
'e0aab12d7b61a51f527a9a41f6c1687f' +
|
||
'e2537298ca2a8f5946f8e5fd091dbdcb',
|
||
'00000011');
|
||
HexToHugeWordB(
|
||
'00EB7A19ACE9E3006350E329504B45E2CA82310B26DCD87D5C68F1EEA8F55267C31B2E8BB4251F84' +
|
||
'D7E0B2C04626F5AFF93EDCFB25C9C2B3FF8AE10E839A2DDB4CDCFE4FF47728B4A1B7C1362BAAD29A' +
|
||
'B48D2869D5024121435811591BE392F982FB3E87D095AEB40448DB972F3AC14F7BC275195281CE32' +
|
||
'D2F1B76D4D353E2D', EM);
|
||
RSAEncryptMessage(Pub, EM, CM);
|
||
Assert(HugeWordToHexB(CM) =
|
||
'1253E04DC0A5397BB44A7AB87E9BF2A039A33D1E996FC82A94CCD30074C95DF763722017069E5268' +
|
||
'DA5D1C0B4F872CF653C11DF82314A67968DFEAE28DEF04BB6D84B1C31D654A1970E5783BD6EB96A0' +
|
||
'24C2CA2F4A90FE9F2EF5C9C140E5BB48DA9536AD8700C84FC9130ADEA74E558D51A74DDF85D8B50D' +
|
||
'E96838D6063E0955');
|
||
|
||
RSAPublicKeyFinalise(Pub);
|
||
HugeWordFinalise(CM);
|
||
HugeWordFinalise(EM);
|
||
end;
|
||
|
||
var Pri : TRSAPrivateKey;
|
||
Pub : TRSAPublicKey;
|
||
|
||
procedure TestStr(const EncryptionType: TRSAEncryptionType; const Pln: RawByteString);
|
||
var Enc, Dec : RawByteString;
|
||
begin
|
||
Enc := RSAEncryptStr(EncryptionType, Pub, Pln);
|
||
Assert(Enc <> Pln);
|
||
Dec := RSADecryptStr(EncryptionType, Pri, Enc);
|
||
Assert(Dec = Pln);
|
||
end;
|
||
|
||
procedure TestCase1;
|
||
begin
|
||
RSAPrivateKeyAssignHex(Pri,
|
||
1024,
|
||
'bbf82f090682ce9c2338ac2b9da871f7' +
|
||
'368d07eed41043a440d6b6f07454f51f' +
|
||
'b8dfbaaf035c02ab61ea48ceeb6fcd48' +
|
||
'76ed520d60e1ec4619719d8a5b8b807f' +
|
||
'afb8e0a3dfc737723ee6b4b7d93a2584' +
|
||
'ee6a649d060953748834b2454598394e' +
|
||
'e0aab12d7b61a51f527a9a41f6c1687f' +
|
||
'e2537298ca2a8f5946f8e5fd091dbdcb',
|
||
'a5dafc5341faf289c4b988db30c1cdf8' +
|
||
'3f31251e0668b42784813801579641b2' +
|
||
'9410b3c7998d6bc465745e5c392669d6' +
|
||
'870da2c082a939e37fdcb82ec93edac9' +
|
||
'7ff3ad5950accfbc111c76f1a9529444' +
|
||
'e56aaf68c56c092cd38dc3bef5d20a93' +
|
||
'9926ed4f74a13eddfbe1a1cecc4894af' +
|
||
'9428c2b7b8883fe4463a4bc85b1cb3c1');
|
||
HexToHugeWordB(
|
||
'eecfae81b1b9b3c908810b10a1b56001' +
|
||
'99eb9f44aef4fda493b81a9e3d84f632' +
|
||
'124ef0236e5d1e3b7e28fae7aa040a2d' +
|
||
'5b252176459d1f397541ba2a58fb6599',
|
||
Pri.Prime1);
|
||
HexToHugeWordB(
|
||
'c97fb1f027f453f6341233eaaad1d935' +
|
||
'3f6c42d08866b1d05a0f2035028b9d86' +
|
||
'9840b41666b42e92ea0da3b43204b5cf' +
|
||
'ce3352524d0416a5a441e700af461503',
|
||
Pri.Prime2);
|
||
RSAPublicKeyAssignHex(Pub,
|
||
1024,
|
||
'bbf82f090682ce9c2338ac2b9da871f7' +
|
||
'368d07eed41043a440d6b6f07454f51f' +
|
||
'b8dfbaaf035c02ab61ea48ceeb6fcd48' +
|
||
'76ed520d60e1ec4619719d8a5b8b807f' +
|
||
'afb8e0a3dfc737723ee6b4b7d93a2584' +
|
||
'ee6a649d060953748834b2454598394e' +
|
||
'e0aab12d7b61a51f527a9a41f6c1687f' +
|
||
'e2537298ca2a8f5946f8e5fd091dbdcb',
|
||
'00000011');
|
||
TestStr(rsaetRSAES_OAEP,
|
||
RawByteString(
|
||
#$d4#$36#$e9#$95#$69#$fd#$32#$a7#$c8#$a0#$5b#$bc#$90#$d3#$2c#$49));
|
||
end;
|
||
|
||
procedure TestCase2;
|
||
begin
|
||
RSAPrivateKeyAssignHex(Pri,
|
||
1024,
|
||
'FED6F2848D95AFACE2354A771792D30E57B0C964D33BD700A53B92FCE0EFC7ADE2DEBC947FE762BD' +
|
||
'07F5C803ACB2CC603796E2D12684C1F827B0544575D1EE7E93500F2011A853EBBFECA78781D29D6D' +
|
||
'46B347FE76F209C0F4B9F1D457843B432CEA8060369748D858222F773758BAB16301345B02AEC17B' +
|
||
'2C09E7CE9D37F4C5',
|
||
'89DCC2AA0EE65179578EB8D020829F86FCCD78C600B838A1F2C17DCD2BEACBBD382483245AE55437' +
|
||
'2B1D3DAD2F3A32F242607027F18C945AA92DED08FEAA293860221F0838132036F833B2203EDA26C1' +
|
||
'5C90664A5627E3C6B1B4EF621FE34D239AD144F28C4891BF0354FE1D5C2FBA62B3F9A4793B835B5B' +
|
||
'6A1A3EE0AF995E69');
|
||
RSAPublicKeyAssignHex(Pub,
|
||
1024,
|
||
'FED6F2848D95AFACE2354A771792D30E57B0C964D33BD700A53B92FCE0EFC7ADE2DEBC947FE762BD' +
|
||
'07F5C803ACB2CC603796E2D12684C1F827B0544575D1EE7E93500F2011A853EBBFECA78781D29D6D' +
|
||
'46B347FE76F209C0F4B9F1D457843B432CEA8060369748D858222F773758BAB16301345B02AEC17B' +
|
||
'2C09E7CE9D37F4C5',
|
||
'00010001');
|
||
TestStr(rsaetRSAES_PKCS1, '');
|
||
TestStr(rsaetRSAES_OAEP, '');
|
||
TestStr(rsaetRSAES_PKCS1, 'Fundamentals');
|
||
TestStr(rsaetRSAES_OAEP, '12345678901234567890123456789012345678901234567890');
|
||
end;
|
||
|
||
procedure TestSign;
|
||
var Msg : RawByteString;
|
||
Hash : T256BitDigest;
|
||
Sign : RawByteString;
|
||
L : Integer;
|
||
begin
|
||
RSAPrivateKeyAssignHex(Pri,
|
||
1024,
|
||
'FED6F2848D95AFACE2354A771792D30E57B0C964D33BD700A53B92FCE0EFC7ADE2DEBC947FE762BD' +
|
||
'07F5C803ACB2CC603796E2D12684C1F827B0544575D1EE7E93500F2011A853EBBFECA78781D29D6D' +
|
||
'46B347FE76F209C0F4B9F1D457843B432CEA8060369748D858222F773758BAB16301345B02AEC17B' +
|
||
'2C09E7CE9D37F4C5',
|
||
'89DCC2AA0EE65179578EB8D020829F86FCCD78C600B838A1F2C17DCD2BEACBBD382483245AE55437' +
|
||
'2B1D3DAD2F3A32F242607027F18C945AA92DED08FEAA293860221F0838132036F833B2203EDA26C1' +
|
||
'5C90664A5627E3C6B1B4EF621FE34D239AD144F28C4891BF0354FE1D5C2FBA62B3F9A4793B835B5B' +
|
||
'6A1A3EE0AF995E69');
|
||
RSAPublicKeyAssignHex(Pub,
|
||
1024,
|
||
'FED6F2848D95AFACE2354A771792D30E57B0C964D33BD700A53B92FCE0EFC7ADE2DEBC947FE762BD' +
|
||
'07F5C803ACB2CC603796E2D12684C1F827B0544575D1EE7E93500F2011A853EBBFECA78781D29D6D' +
|
||
'46B347FE76F209C0F4B9F1D457843B432CEA8060369748D858222F773758BAB16301345B02AEC17B' +
|
||
'2C09E7CE9D37F4C5',
|
||
'00010001');
|
||
|
||
Msg := 'Test message 123';
|
||
Hash := CalcSHA256(Msg);
|
||
SetLength(Sign, 128);
|
||
L := RSASignMessage(rsastMessage, rsahfSHA256, Pri, Hash, SizeOf(Hash), Sign[1], Length(Sign));
|
||
Assert(L = 128);
|
||
Assert(RSACheckSignature(rsastMessage, Pub, Hash, SizeOf(Hash), Sign[1], L));
|
||
end;
|
||
|
||
begin
|
||
RSAPrivateKeyInit(Pri);
|
||
RSAPublicKeyInit(Pub);
|
||
|
||
TestMGF;
|
||
TestEncrypt;
|
||
TestCase1;
|
||
TestCase2;
|
||
TestSign;
|
||
|
||
RSAPublicKeyFinalise(Pub);
|
||
RSAPrivateKeyFinalise(Pri);
|
||
end;
|
||
{$ENDIF}
|
||
|
||
{$IFDEF OS_WIN}
|
||
{$IFDEF CIPHER_PROFILE}
|
||
procedure Profile;
|
||
const KeySize = 1024 + 512;
|
||
var T : Word32;
|
||
Pri, Pr2 : TRSAPrivateKey;
|
||
Pub, Pu2 : TRSAPublicKey;
|
||
Pln, Enc, Dec : RawByteString;
|
||
begin
|
||
SetRandomSeed($12345679);
|
||
|
||
RSAPrivateKeyInit(Pri);
|
||
RSAPublicKeyInit(Pub);
|
||
|
||
RSAPrivateKeyInit(Pr2);
|
||
RSAPublicKeyInit(Pu2);
|
||
|
||
T := GetTickCount;
|
||
//RSAGenerateKeys(KeySize, Pr2, Pu2);
|
||
RSAGenerateKeys(KeySize, Pri, Pub);
|
||
T := GetTickCount - T;
|
||
{ Pri.KeySize := Pu2.KeySize;
|
||
Pri.Modulus := Pu2.Modulus;
|
||
Pri.Exponent := Pu2.Exponent;
|
||
Pub.KeySize := Pr2.KeySize;
|
||
Pub.Modulus := Pr2.Modulus;
|
||
Pub.Exponent := Pr2.Exponent; }
|
||
Writeln('GenerateKeys: ', T / 1000.0:0:2, 's');
|
||
Writeln('Pri.Mod:');
|
||
Writeln(HugeWordToHexB(Pri.Modulus));
|
||
Writeln('Pri.Exp:');
|
||
Writeln(HugeWordToHexB(Pri.Exponent));
|
||
Writeln('Pub.Mod:');
|
||
Writeln(HugeWordToHexB(Pub.Modulus));
|
||
Writeln('Pub.Exp:');
|
||
Writeln(HugeWordToHexB(Pub.Exponent));
|
||
|
||
T := GetTickCount;
|
||
Pln := '123456';
|
||
Enc := RSAEncryptStr(rsaetRSAES_PKCS1, Pub, Pln);
|
||
Assert(Enc <> Pln);
|
||
T := GetTickCount - T;
|
||
Writeln('EncryptStr: ', T, 'ms');
|
||
|
||
T := GetTickCount;
|
||
Dec := RSADecryptStr(rsaetRSAES_PKCS1, Pri, Enc);
|
||
Assert(Dec = Pln);
|
||
T := GetTickCount - T;
|
||
Writeln('DecryptStr: ', T, 'ms');
|
||
|
||
RSAPublicKeyFinalise(Pub);
|
||
RSAPrivateKeyFinalise(Pri);
|
||
end;
|
||
{$ENDIF}
|
||
{$ENDIF}
|
||
|
||
|
||
|
||
end.
|
||
|