diff --git a/doc/sql.extensions/README.builtin_functions.txt b/doc/sql.extensions/README.builtin_functions.txt index 462da7625e..b54bac348d 100644 --- a/doc/sql.extensions/README.builtin_functions.txt +++ b/doc/sql.extensions/README.builtin_functions.txt @@ -12,6 +12,7 @@ Authors: Oleg Loa Alexey Karyakin Claudio Valderrama C. + Alexander Peshkov --- @@ -415,6 +416,40 @@ Example: select decode(state, 0, 'deleted', 1, 'active', 'unknown') from things; +------------------- +ENCRYPT and DECRYPT +------------------- + +Function: + Encrypts/decrypts data using symmetric cipher. + +Format: + {ENCRYPT | DECRYPT} ( USING [MODE ] KEY \ + [IV ] [] [CTR_LENGTH ] [COUNTER ]) + + algorithm ::= { block_cipher | stream_cipher } + block_cipher ::= { AES | ANUBIS | BLOWFISH | KHAZAD | RC5 | RC6 | SAFER+ | TWOFISH | XTEA } + stream_cipher ::= { CHACHA20 | RC4 | SOBER128 } + mode ::= { CBC | CFB | CTR | ECB | OFB } + endianness ::= { CTR_BIG_ENDIAN | CTR_LITTLE_ENDIAN } + +Important: + - Mode should be specified for block ciphers. + - Initialization vector (IV) should be specified for block ciphers in all modes except ECB and + all stream ciphers except RC4. + - Endianness may be specified only in CTR mode, default is little endian counter. + - Counter length (CTR_LENGTH, bytes) may be specified only in CTR mode, default is the size of IV. + - Initial counter value (COUNTER) may be specified only for CHACHA20 cipher, default is 0. + - Sizes of data strings passed to this functions are according to selected algorithm and mode + requirements. + - Functions return BLOB when first argument is blob and varbinary for all text types. + +Example: + select encrypt('897897' using sober128 key 'AbcdAbcdAbcdAbcd' iv '01234567') from rdb$database; + select decrypt(x'0154090759DF' using sober128 key 'AbcdAbcdAbcdAbcd' iv '01234567') from rdb$database; + select decrypt(secret_field using aes mode ofb key '0123456701234567' iv init_vector) from secure_table; + + --- EXP --- @@ -872,6 +907,119 @@ Example: select rpad(x, 10) from y; +----------- +RSA_PRIVATE +----------- + +Function: + Returns RSA private key of specified length (in bytes) in PKCS#1 format as VARBINARY atring. + +Format: + RSA_PRIVATE ( ) + +Example: + select rdb$set_context('USER_SESSION', 'private_key', rsa_private(256)) from rdb$database; + + +---------- +RSA_PUBLIC +---------- + +Function: + Returns RSA public key for specified RSA private key, all keys are in PKCS#1 format. + +Format: + RSA_PUBLIC ( ) + +Example: + (tip - start running samples one by one from RSA_PRIVATE function) + select rdb$set_context('USER_SESSION', 'public_key', + rsa_public(rdb$get_context('USER_SESSION', 'private_key'))) from rdb$database; + + +----------- +RSA_ENCRYPT +----------- + +Function: + Pads data using OAEP padding and encrypts using RSA public key. Normally used to encrypt + short symmetric keys which are then used in block ciphers to encrypt a message. + +Format: + RSA_ENCRYPT ( KEY [LPARAM ] [HASH ] ) + KEY should be a value, returhed by RSA_PUBLIC function. + LPARAM is an additional system specific tag that can be applied to identify which + system encoded the message. Default value is NULL. + hash ::= { MD5 | SHA1 | SHA256 | SHA512 } Default is SHA256. + +Example: + (tip - start running samples one by one from RSA_PRIVATE function) + select rdb$set_context('USER_SESSION', 'msg', rsa_encrypt('Some message' + key rdb$get_context('USER_SESSION', 'public_key'))) from rdb$database; + + +----------- +RSA_DECRYPT +----------- + +Function: + Decrypts using RSA private key and OAEP de-pads the resulting data. + +Format: + RSA_DECRYPT ( KEY [LPARAM ] [HASH ] ) + KEY should be a value, returhed by RSA_PRIVATE function. + LPARAM is the same variable passed to RSA_ENCRYPT. If it does not match + what was used during encoding this function will not decrypt the packet. + hash ::= { MD5 | SHA1 | SHA256 | SHA512 } Default is SHA256. + +Example: + (tip - start running samples one by one from RSA_PRIVATE function) + select rsa_decrypt(rdb$get_context('USER_SESSION', 'msg') + key rdb$get_context('USER_SESSION', 'private_key')) from rdb$database; + + +-------- +RSA_SIGN +-------- + +Function: + Performs PSS encoding of message digest to be signed and signs using RSA private key. + +Format: + RSA_SIGN ( KEY [HASH ] [SALT_LENGTH ] ) + KEY should be a value, returhed by RSA_PRIVATE function. + hash ::= { MD5 | SHA1 | SHA256 | SHA512 } Default is SHA256. + SALT_LENGTH indicates the length of the desired salt, and should typically be small. + A good value is between 8 and 16. + +Example: + (tip - start running samples one by one from RSA_PRIVATE function) + select rdb$set_context('USER_SESSION', 'msg', rsa_sign(hash('Test message' using sha256) + key rdb$get_context('USER_SESSION', 'private_key'))) from rdb$database; + + +---------- +RSA_VERIFY +---------- + +Function: + Performs PSS encoding of message digest to be signed and verifies it's digital signature + using RSA public key. + +Format: + RSA_VERIFY ( SIGNATURE KEY [HASH ] [SALT_LENGTH ] ) + SIGNATURE should be a value, returhed by RSA_SIGN function. + KEY should be a value, returhed by RSA_PUBLIC function. + hash ::= { MD5 | SHA1 | SHA256 | SHA512 } Default is SHA256. + SALT_LENGTH indicates the length of the desired salt, and should typically be small. + A good value is between 8 and 16. + +Example: + (tip - start running samples one by one from RSA_PRIVATE function) + select rsa_verify(hash('Test message' using sha256) signature rdb$get_context('USER_SESSION', 'msg') + key rdb$get_context('USER_SESSION', 'public_key')) from rdb$database; + + ---- SIGN ---- diff --git a/src/common/classes/fb_string.cpp b/src/common/classes/fb_string.cpp index 01d1ee68ce..0468bba268 100644 --- a/src/common/classes/fb_string.cpp +++ b/src/common/classes/fb_string.cpp @@ -28,6 +28,7 @@ #include "firebird.h" #include "../common/classes/fb_string.h" +#include "../common/classes/MetaName.h" #include #include @@ -95,6 +96,13 @@ namespace Firebird memcpy(stringBuffer, v.c_str(), v.length()); } + AbstractString::AbstractString(const size_type limit, const MetaName& v) + : max_length(static_cast(limit)) + { + initialize(v.length()); + memcpy(stringBuffer, v.c_str(), v.length()); + } + AbstractString::AbstractString(const size_type limit, const size_type sizeL, const void* dataL) : max_length(static_cast(limit)) { diff --git a/src/common/classes/fb_string.h b/src/common/classes/fb_string.h index 56a94c84f8..f9d6c1b5ca 100644 --- a/src/common/classes/fb_string.h +++ b/src/common/classes/fb_string.h @@ -41,6 +41,8 @@ namespace Firebird { + class MetaName; + class AbstractString : private AutoStorage { public: @@ -163,6 +165,7 @@ namespace Firebird const_pointer p2, const size_type n2); AbstractString(const size_type limit, const AbstractString& v); + AbstractString(const size_type limit, const MetaName& v); explicit AbstractString(const size_type limit) : max_length(static_cast(limit)), @@ -644,6 +647,7 @@ namespace Firebird public: StringBase() : AbstractString(Comparator::getMaxLength()) {} StringBase(const StringType& v) : AbstractString(Comparator::getMaxLength(), v) {} + explicit StringBase(const MetaName& v) : AbstractString(Comparator::getMaxLength(), v) {} StringBase(const void* s, size_type n) : AbstractString(Comparator::getMaxLength(), n, s) {} StringBase(const_pointer s) : AbstractString(Comparator::getMaxLength(), static_cast(strlen(s)), s) {} explicit StringBase(const unsigned char* s) : diff --git a/src/common/cvt.cpp b/src/common/cvt.cpp index 88a794fe32..f41b838ca0 100644 --- a/src/common/cvt.cpp +++ b/src/common/cvt.cpp @@ -3050,6 +3050,47 @@ DecimalFixed CVT_get_dec_fixed(const dsc* desc, SSHORT scale, DecimalStatus decS } +const UCHAR* CVT_get_bytes(const dsc* desc, unsigned& size) +{ +/************************************** + * + * C V T _ g e t _ b y t e s + * + ************************************** + * + * Functional description + * Return raw data of descriptor. + * + **************************************/ + if (!desc) + { + size = 0; + return nullptr; + } + + switch (desc->dsc_dtype) + { + case dtype_varying: + { + vary* v = (vary*)(desc->dsc_address); + size = v->vary_length; + return (const UCHAR*)v->vary_string; + } + + case dtype_cstring: + size = strlen((const char*)desc->dsc_address); + return desc->dsc_address; + + default: + size = desc->dsc_length; + return desc->dsc_address; + break; + } + + return nullptr; // compiler warning silencer +} + + SQUAD CVT_get_quad(const dsc* desc, SSHORT scale, DecimalStatus decSt, ErrorFunction err) { /************************************** diff --git a/src/common/cvt.h b/src/common/cvt.h index 7d7491c54a..cb72af38e8 100644 --- a/src/common/cvt.h +++ b/src/common/cvt.h @@ -102,5 +102,6 @@ SINT64 CVT_get_int64(const dsc*, SSHORT, Firebird::DecimalStatus, ErrorFunction) SQUAD CVT_get_quad(const dsc*, SSHORT, Firebird::DecimalStatus, ErrorFunction); void CVT_string_to_datetime(const dsc*, ISC_TIMESTAMP_TZ*, bool*, const Firebird::EXPECT_DATETIME, bool, Firebird::Callbacks*); +const UCHAR* CVT_get_bytes(const dsc*, unsigned&); #endif //COMMON_CVT_H diff --git a/src/dsql/parse.y b/src/dsql/parse.y index 4010c1561e..e735b11e87 100644 --- a/src/dsql/parse.y +++ b/src/dsql/parse.y @@ -596,19 +596,26 @@ using namespace Firebird; %token BIND %token COMPARE_DECFLOAT %token CUME_DIST +%token COUNTER +%token CTR_BIG_ENDIAN +%token CTR_LENGTH +%token CTR_LITTLE_ENDIAN %token DECFLOAT %token DEFINER %token EXCLUDE %token FIRST_DAY %token FOLLOWING %token IDLE +%token IV %token INVOKER %token LAST_DAY %token LEGACY %token LOCAL %token LOCALTIME %token LOCALTIMESTAMP +%token LPARAM %token MESSAGE +%token MODE %token NATIVE %token NORMALIZE_DECFLOAT %token NTILE @@ -623,6 +630,14 @@ using namespace Firebird; %token RDB_ROLE_IN_USE %token RDB_SYSTEM_PRIVILEGE %token RESET +%token RSA_DECRYPT +%token RSA_ENCRYPT +%token RSA_PRIVATE +%token RSA_PUBLIC +%token RSA_SIGN +%token RSA_VERIFY +%token SALT_LENGTH +%token SIGNATURE %token SECURITY %token SESSION %token SQL @@ -7906,6 +7921,8 @@ system_function_std_syntax | RIGHT | ROUND | RPAD + | RSA_PRIVATE + | RSA_PUBLIC | SIGN | SIN | SINH @@ -7946,6 +7963,14 @@ system_function_special_syntax newNode(MAKE_const_slong($3))->add($5)->add($7)); $$->dsqlSpecialSyntax = true; } + | encrypt_decrypt '(' value USING valid_symbol_name opt_mode KEY value opt_iv opt_counter_type opt_counter ')' + { + $$ = newNode(*$1, + newNode($3)->add(MAKE_str_constant(newIntlString($5->c_str()), CS_ASCII))-> + add(MAKE_str_constant(newIntlString($6->c_str()), CS_ASCII))->add($8)->add($9)-> + add(MAKE_str_constant(newIntlString($10->c_str()), CS_ASCII))->add($11)); + $$->dsqlSpecialSyntax = true; + } | FIRST_DAY '(' of_first_last_day_part FROM value ')' { $$ = newNode(*$1, @@ -7985,6 +8010,27 @@ system_function_special_syntax } | POSITION '(' value_list_opt ')' { $$ = newNode(*$1, $3); } + | rsa_encrypt_decrypt '(' value KEY value opt_lparam opt_hash ')' + { + $$ = newNode(*$1, + newNode($3)->add($5)->add($6)-> + add(MAKE_str_constant(newIntlString($7->c_str()), CS_ASCII))); + $$->dsqlSpecialSyntax = true; + } + | RSA_SIGN '(' value KEY value opt_hash opt_saltlen ')' + { + $$ = newNode(*$1, + newNode($3)->add($5)-> + add(MAKE_str_constant(newIntlString($6->c_str()), CS_ASCII))->add($7)); + $$->dsqlSpecialSyntax = true; + } + | RSA_VERIFY'(' value SIGNATURE value KEY value opt_hash opt_saltlen ')' + { + $$ = newNode(*$1, + newNode($3)->add($5)->add($7)-> + add(MAKE_str_constant(newIntlString($8->c_str()), CS_ASCII))->add($9)); + $$->dsqlSpecialSyntax = true; + } | RDB_SYSTEM_PRIVILEGE '(' valid_symbol_name ')' { ValueExprNode* v = MAKE_system_privilege($3->c_str()); @@ -7992,6 +8038,82 @@ system_function_special_syntax } ; +%type rsa_encrypt_decrypt +rsa_encrypt_decrypt + : RSA_DECRYPT | RSA_ENCRYPT + ; + +%type opt_lparam +opt_lparam + : LPARAM value + { $$ = $2; } + | /* nothing */ + { $$ = MAKE_str_constant(newIntlString(""), CS_ASCII); } + ; + +%type opt_hash +opt_hash + : HASH valid_symbol_name + { $$ = $2; } + | /* nothing */ + { $$ = newNode(""); } + ; + +%type opt_saltlen +opt_saltlen + : SALT_LENGTH value + { $$ = $2; } + | /* nothing */ + { $$ = MAKE_str_constant(newIntlString(""), CS_ASCII); } + ; + +%type opt_mode +opt_mode + : MODE valid_symbol_name + { $$ = $2; } + | /* nothing */ + { $$ = newNode(""); } + ; + +%type opt_iv +opt_iv + : IV value + { $$ = $2; } + | /* nothing */ + { $$ = MAKE_str_constant(newIntlString(""), CS_ASCII); } + ; + +%type opt_counter_type +opt_counter_type + : counter_type + { $$ = $1; } + | /* nothing */ + { $$ = newNode(""); } + ; + +%type counter_type +counter_type + : CTR_BIG_ENDIAN | CTR_LITTLE_ENDIAN + ; + +%type opt_counter +opt_counter + : counter_name value + { $$ = $2; } + | /* nothing */ + { $$ = MAKE_str_constant(newIntlString(""), CS_ASCII); } + ; + +%type counter_name +counter_name + : COUNTER | CTR_LENGTH + ; + +%type encrypt_decrypt +encrypt_decrypt + : ENCRYPT | DECRYPT + ; + %type of_first_last_day_part of_first_last_day_part : OF YEAR { $$ = blr_extract_year; } @@ -8682,6 +8804,21 @@ non_reserved_word | TOTALORDER | TRAPS | ZONE + | MODE // crypt functions + | IV + | COUNTER + | CTR_BIG_ENDIAN + | CTR_LITTLE_ENDIAN + | CTR_LENGTH + | LPARAM + | RSA_DECRYPT + | RSA_ENCRYPT + | RSA_PRIVATE + | RSA_PUBLIC + | RSA_SIGN + | RSA_VERIFY + | SALT_LENGTH + | SIGNATURE ; %% diff --git a/src/jrd/SysFunction.cpp b/src/jrd/SysFunction.cpp index b3361e8818..efddfd691f 100644 --- a/src/jrd/SysFunction.cpp +++ b/src/jrd/SysFunction.cpp @@ -25,6 +25,7 @@ * Contributor(s): ______________________________________. * Oleg Loa * Alexey Karyakin + * Alexander Peshkov * */ @@ -56,8 +57,16 @@ #include "../jrd/Collation.h" #include "../common/classes/FpeControl.h" #include "../jrd/extds/ExtDS.h" + #include +#ifndef WIN_NT +#define LTC_PTHREAD +#endif +#define USE_LTM +#define LTM_DESC +#include + using namespace Firebird; using namespace Jrd; @@ -179,6 +188,11 @@ void setParamsAsciiVal(DataTypeUtilBase* dataTypeUtil, const SysFunction* functi void setParamsCharToUuid(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, int argsCount, dsc** args); void setParamsDateAdd(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, int argsCount, dsc** args); void setParamsDateDiff(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, int argsCount, dsc** args); +void setParamsEncrypt(DataTypeUtilBase*, const SysFunction*, int argsCount, dsc** args); +void setParamsRsaEncrypt(DataTypeUtilBase*, const SysFunction*, int argsCount, dsc** args); +void setParamsRsaPublic(DataTypeUtilBase*, const SysFunction*, int argsCount, dsc** args); +void setParamsRsaSign(DataTypeUtilBase*, const SysFunction*, int argsCount, dsc** args); +void setParamsRsaVerify(DataTypeUtilBase*, const SysFunction*, int argsCount, dsc** args); void setParamsFirstLastDay(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, int argsCount, dsc** args); void setParamsGetSetContext(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, int argsCount, dsc** args); void setParamsOverlay(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, int argsCount, dsc** args); @@ -195,6 +209,7 @@ void makeInt64Result(DataTypeUtilBase* dataTypeUtil, const SysFunction* function void makeLongResult(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args); ///void makeLongStringOrBlobResult(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args); void makeShortResult(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args); +void makeBoolResult(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args); // specific make functions void makeAbs(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args); @@ -203,6 +218,10 @@ void makeBin(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* r void makeBinShift(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args); void makeCeilFloor(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args); void makeDateAdd(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args); +void makeEncrypt(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args); +void makeDecrypt(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args); +void makeRsaEncrypt(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args); +void makeRsaDecrypt(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args); void makeFirstLastDayResult(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args); void makeGetSetContext(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args); void makeGetTranCN(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args); @@ -215,6 +234,9 @@ void makePi(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* re void makeReplace(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args); void makeReverse(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args); void makeRound(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args); +void makeRsaPrivate(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args); +void makeRsaPublic(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args); +void makeRsaSign(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args); void makeTrunc(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args); void makeUuid(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args); void makeUuidToChar(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args); @@ -233,6 +255,14 @@ dsc* evlCeil(thread_db* tdbb, const SysFunction* function, const NestValueArray& dsc* evlCharToUuid(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, impure_value* impure); dsc* evlDateAdd(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, impure_value* impure); dsc* evlDateDiff(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, impure_value* impure); +dsc* evlDecrypt(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, impure_value* impure); +dsc* evlEncrypt(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, impure_value* impure); +dsc* evlRsaDecrypt(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, impure_value* impure); +dsc* evlRsaEncrypt(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, impure_value* impure); +dsc* evlRsaPrivate(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, impure_value* impure); +dsc* evlRsaPublic(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, impure_value* impure); +dsc* evlRsaSign(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, impure_value* impure); +dsc* evlRsaVerify(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, impure_value* impure); dsc* evlExp(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, impure_value* impure); dsc* evlFirstLastDay(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, impure_value* impure); dsc* evlFloor(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, impure_value* impure); @@ -355,6 +385,17 @@ double fbcot(double value) throw() } +void tomCheck(int err, const char* text) +{ + if (err == CRYPT_OK) + return; + + string buf; + buf.printf("LibTomCrypt error %s: %s", text, error_to_string(err)); + ERR_post(Arg::Gds(isc_random) << buf); +} + + bool initResult(dsc* result, int argsCount, const dsc** args, bool* isNullable) { *isNullable = false; @@ -549,6 +590,110 @@ void setParamsDateDiff(DataTypeUtilBase*, const SysFunction*, int argsCount, dsc } +const unsigned CRYPT_ARG_VALUE = 0; +const unsigned CRYPT_ARG_ALGORITHM = 1; +const unsigned CRYPT_ARG_MODE = 2; +const unsigned CRYPT_ARG_KEY = 3; +const unsigned CRYPT_ARG_IV = 4; +const unsigned CRYPT_ARG_CTRTYPE = 5; +const unsigned CRYPT_ARG_COUNTER = 6; +const unsigned CRYPT_ARG_MAX = 7; + +void setParamsEncrypt(DataTypeUtilBase*, const SysFunction*, int argsCount, dsc** args) +{ + fb_assert(argsCount == CRYPT_ARG_MAX); + + if (!args[CRYPT_ARG_VALUE]->isBlob()) + args[CRYPT_ARG_VALUE]->makeVarying(args[CRYPT_ARG_VALUE]->getStringLength(), ttype_binary); + + fb_assert(args[CRYPT_ARG_ALGORITHM]->dsc_address && args[CRYPT_ARG_ALGORITHM]->isText()); + + args[CRYPT_ARG_KEY]->makeVarying(args[CRYPT_ARG_KEY]->getStringLength(), ttype_binary); + + if (args[CRYPT_ARG_IV]->dsc_length) + args[CRYPT_ARG_IV]->makeVarying(args[CRYPT_ARG_IV]->getStringLength(), ttype_binary); + + if (args[CRYPT_ARG_CTRTYPE]->dsc_length) + args[CRYPT_ARG_CTRTYPE]->makeVarying(args[CRYPT_ARG_CTRTYPE]->getStringLength(), ttype_ascii); + + if (args[CRYPT_ARG_COUNTER]->dsc_length) + args[CRYPT_ARG_COUNTER]->makeInt64(0); +} + + +const unsigned RSA_CRYPT_ARG_VALUE = 0; +const unsigned RSA_CRYPT_ARG_KEY = 1; +const unsigned RSA_CRYPT_ARG_LPARAM = 2; +const unsigned RSA_CRYPT_ARG_HASH = 3; +const unsigned RSA_CRYPT_ARG_MAX = 4; + +void setParamsRsaEncrypt(DataTypeUtilBase*, const SysFunction*, int argsCount, dsc** args) +{ + fb_assert(argsCount == RSA_CRYPT_ARG_MAX); + + args[RSA_CRYPT_ARG_VALUE]->makeVarying(args[RSA_CRYPT_ARG_VALUE]->getStringLength(), ttype_binary); + args[RSA_CRYPT_ARG_KEY]->makeVarying(args[RSA_CRYPT_ARG_KEY]->getStringLength(), ttype_binary); + + if (args[RSA_CRYPT_ARG_LPARAM]->dsc_length) + args[RSA_CRYPT_ARG_LPARAM]->makeVarying(args[RSA_CRYPT_ARG_LPARAM]->getStringLength(), ttype_binary); + + if (args[RSA_CRYPT_ARG_HASH]->dsc_length) + args[RSA_CRYPT_ARG_HASH]->makeVarying(args[RSA_CRYPT_ARG_HASH]->getStringLength(), ttype_binary); +} + + +const unsigned RSA_SIGN_ARG_VALUE = 0; +const unsigned RSA_SIGN_ARG_KEY = 1; +const unsigned RSA_SIGN_ARG_HASH = 2; +const unsigned RSA_SIGN_ARG_SALTLEN = 3; +const unsigned RSA_SIGN_ARG_MAX = 4; + +void setParamsRsaSign(DataTypeUtilBase*, const SysFunction*, int argsCount, dsc** args) +{ + fb_assert(argsCount == RSA_SIGN_ARG_MAX); + + args[RSA_SIGN_ARG_VALUE]->makeVarying(args[RSA_SIGN_ARG_VALUE]->getStringLength(), ttype_binary); + args[RSA_SIGN_ARG_KEY]->makeVarying(args[RSA_SIGN_ARG_KEY]->getStringLength(), ttype_binary); + + if (args[RSA_SIGN_ARG_HASH]->dsc_length) + args[RSA_SIGN_ARG_HASH]->makeVarying(args[RSA_SIGN_ARG_HASH]->getStringLength(), ttype_binary); + + if (args[RSA_SIGN_ARG_SALTLEN]->dsc_length) + args[RSA_SIGN_ARG_SALTLEN]->makeShort(0); +} + + +const unsigned RSA_VERIFY_ARG_VALUE = 0; +const unsigned RSA_VERIFY_ARG_SIGNATURE = 1; +const unsigned RSA_VERIFY_ARG_KEY = 2; +const unsigned RSA_VERIFY_ARG_HASH = 3; +const unsigned RSA_VERIFY_ARG_SALTLEN = 4; +const unsigned RSA_VERIFY_ARG_MAX = 5; + +void setParamsRsaVerify(DataTypeUtilBase*, const SysFunction*, int argsCount, dsc** args) +{ + fb_assert(argsCount == RSA_VERIFY_ARG_MAX); + + args[RSA_VERIFY_ARG_VALUE]->makeVarying(args[RSA_VERIFY_ARG_VALUE]->getStringLength(), ttype_binary); + args[RSA_VERIFY_ARG_KEY]->makeVarying(args[RSA_VERIFY_ARG_KEY]->getStringLength(), ttype_binary); + args[RSA_VERIFY_ARG_SIGNATURE]->makeVarying(args[RSA_VERIFY_ARG_SIGNATURE]->getStringLength(), ttype_binary); + + if (args[RSA_VERIFY_ARG_HASH]->dsc_length) + args[RSA_VERIFY_ARG_HASH]->makeVarying(args[RSA_VERIFY_ARG_HASH]->getStringLength(), ttype_binary); + + if (args[RSA_VERIFY_ARG_SALTLEN]->dsc_length) + args[RSA_VERIFY_ARG_SALTLEN]->makeShort(0); +} + + +void setParamsRsaPublic(DataTypeUtilBase*, const SysFunction*, int argsCount, dsc** args) +{ + fb_assert(argsCount == 1); + + args[0]->makeVarying(args[0]->getStringLength(), ttype_binary); +} + + void setParamsFirstLastDay(DataTypeUtilBase*, const SysFunction*, int argsCount, dsc** args) { if (argsCount >= 2) @@ -776,6 +921,46 @@ void makeShortResult(DataTypeUtilBase*, const SysFunction*, dsc* result, } +void makeBoolResult(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, + dsc* result, int argsCount, const dsc** args) +{ + result->makeBoolean(); +} + + +void makeVarBinary(dsc* result, int argsCount, const dsc** args, unsigned length) +{ + result->makeVarying(length, ttype_binary); + + bool isNullable; + if (initResult(result, argsCount > 2 ? 2 : argsCount, args, &isNullable)) + return; + + result->setNullable(isNullable); +} + + +void makeRsaPrivate(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, + dsc* result, int argsCount, const dsc** args) +{ + makeVarBinary(result, argsCount, args, 16 * 1024); +} + + +void makeRsaPublic(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, + dsc* result, int argsCount, const dsc** args) +{ + makeVarBinary(result, argsCount, args, 8 * 1024); +} + + +void makeRsaSign(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, + dsc* result, int argsCount, const dsc** args) +{ + makeVarBinary(result, argsCount, args, 256); +} + + void makeAbs(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args) { @@ -1021,6 +1206,7 @@ void makeGetTranCN(DataTypeUtilBase* /*dataTypeUtil*/, const SysFunction* /*func result->setNullable(true); } + void makeHash(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args) { @@ -1042,6 +1228,52 @@ void makeHash(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* } +void makeEncrypt(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, + int argsCount, const dsc** args) +{ + fb_assert(argsCount == CRYPT_ARG_MAX); + + if (args[0]->isBlob()) + result->makeBlob(0, ttype_binary); + else + result->makeVarying(args[0]->getStringLength(), ttype_binary); + result->setNullable(args[0]->isNullable()); +} + + +void makeDecrypt(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, + int argsCount, const dsc** args) +{ + fb_assert(argsCount == CRYPT_ARG_MAX); + + if (args[0]->isBlob()) + result->makeBlob(0, ttype_none); + else + result->makeVarying(args[0]->getStringLength(), ttype_none); + result->setNullable(args[0]->isNullable()); +} + + +void makeRsaEncrypt(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, + int argsCount, const dsc** args) +{ + fb_assert(argsCount == RSA_CRYPT_ARG_MAX); + + result->makeVarying(256, ttype_binary); + result->setNullable(args[0]->isNullable()); +} + + +void makeRsaDecrypt(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, + int argsCount, const dsc** args) +{ + fb_assert(argsCount == RSA_CRYPT_ARG_MAX); + + result->makeVarying(255, ttype_none); + result->setNullable(args[0]->isNullable()); +} + + void makeLeftRight(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args) { @@ -2127,6 +2359,829 @@ dsc* evlDateAdd(thread_db* tdbb, const SysFunction* function, const NestValueArr return &impure->vlu_desc; } +void raise(const char* x) +{ + (Arg::Gds(isc_random) << x).raise(); +} + + +// Prepare tomcrypt library + +class TomcryptInitializer +{ +public: + explicit TomcryptInitializer(MemoryPool&) + { + ltc_mp = ltm_desc; + + registerCipher(aes_desc); + registerCipher(anubis_desc); + registerCipher(blowfish_desc); + registerCipher(khazad_desc); + registerCipher(rc5_desc); + registerCipher(rc6_desc); + registerCipher(saferp_desc); + registerCipher(twofish_desc); + registerCipher(xtea_desc); + + registerHash(md5_desc); + registerHash(sha1_desc); + registerHash(sha256_desc); + registerHash(sha512_desc); + } + +private: + template + void registerCipher(T& desc) + { + if (register_cipher(&desc) == -1) + raise("Unexpected error registering cipher - probably bad tomcrypt library"); + } + + template + void registerHash(T& desc) + { + if (register_hash(&desc) == -1) + raise("Unexpected error registering hash - probably bad tomcrypt library"); + } +}; + +InitInstance tomcryptInitializer; + + +class PseudoRandom +{ +public: + explicit PseudoRandom(MemoryPool&) + { + // register yarrow + index = register_prng(&yarrow_desc); + if (index == -1) + ERR_post(Arg::Gds(isc_random) << "Error registering PRNG yarrow"); + + // setup the PRNG + tomCheck(yarrow_start(&state), "starting PRNG yarrow"); + tomCheck(rng_make_prng(64, index, &state, NULL), "setting up PRNG yarrow"); + } + + ~PseudoRandom() + { + yarrow_done(&state); + } + + prng_state* getState() + { + return &state; + } + + int getIndex() + { + return index; + } + +private: + int index; + prng_state state; +}; + +InitInstance prng; + + +// Data exchanger between tommath and firebird + +class DataPipe +{ +public: + DataPipe(thread_db* t, const dsc* desc, impure_value* i) + : tdbb(t), + impure(i), + blobMode(desc->isBlob()), + completed(false), + ptr(nullptr), + len(0), + blob(nullptr), + newBlob(nullptr) + { + if (!blobMode) + { + if (!desc->isText()) + raise("Wrong data type for first argument"); + ptr = CVT_get_bytes(desc, len); + } + else + { + blobDesc.makeBlob(0, ttype_none); + blobDesc.dsc_address = (UCHAR*) &impure->vlu_misc.vlu_bid; + + try + { + const UCHAR bpb[] = {isc_bpb_version1, isc_bpb_type, 1, isc_bpb_type_stream}; + newBlob = blb::create2(tdbb, tdbb->getRequest()->req_transaction, &impure->vlu_misc.vlu_bid, + sizeof(bpb), bpb); + blob = blb::open2(tdbb, tdbb->getRequest()->req_transaction, reinterpret_cast(desc->dsc_address), + sizeof(bpb), bpb); + + ptr = inBuf.getBuffer(BLOB_STEP); + len = blob->BLB_get_data(tdbb, inBuf.begin(), inBuf.getCount(), false); + } + catch(...) + { + closeBlobs(); + } + } + } + + ~DataPipe() + { + closeBlobs(); + + if (!completed) + { + dsc result; + result.makeText(0, ttype_none, outBuf.begin()); + EVL_make_value(tdbb, &result, impure); + impure->vlu_desc.setNull(); + } + } + + const UCHAR* from() + { + return ptr; + } + + UCHAR* to() + { + return outBuf.getBuffer(length()); + } + + unsigned length() + { + return len; + } + + bool hasData() + { + return len > 0; + } + + void next() + { + if (hasData()) + { + impure->vlu_desc.clear(); + if (!blobMode) + { + dsc result; + result.makeText(outBuf.getCount(), ttype_binary, outBuf.begin()); + EVL_make_value(tdbb, &result, impure); + + len = 0; + completed = true; + } + else + { + newBlob->BLB_put_data(tdbb, outBuf.begin(), outBuf.getCount()); + + len = blob->BLB_get_data(tdbb, inBuf.begin(), inBuf.getCount(), false); + if (!len) + { + closeBlobs(); + EVL_make_value(tdbb, &blobDesc, impure); + completed = true; + } + } + } + } + +private: + const FB_SIZE_T BLOB_STEP = 1024; + + thread_db* tdbb; + UCharBuffer inBuf, outBuf; + impure_value* impure; + bool blobMode, completed; + const UCHAR* ptr; + unsigned len; + dsc blobDesc; + blb* blob; + blb* newBlob; + + void closeBlobs() + { + if (newBlob) + { + newBlob->BLB_close(tdbb); + newBlob = nullptr; + } + if (blob) + { + blob->BLB_close(tdbb); + blob = nullptr; + } + } +}; + + +// Lists of constant parameter values + +class CodeValue +{ +public: + unsigned code; + const char* value; +}; + +CodeValue* find(CodeValue* array, MetaName& name) +{ + for (; array->value; ++array) + { + if (name == array->value) + return array; + } + + return nullptr; +} + + +dsc* evlEncryptDecrypt(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, + impure_value* impure, bool encryptFlag) +{ + tomcryptInitializer(); + + fb_assert(args.getCount() == CRYPT_ARG_MAX); + + jrd_req* request = tdbb->getRequest(); + + // parse args and check correctness + const dsc* dscs[CRYPT_ARG_MAX]; + for (unsigned i = 0; i < CRYPT_ARG_MAX; ++i) + dscs[i] = EVL_expr(tdbb, request, args[i]); + + MetaName algorithmName, modeName, counterType; + MOV_get_metaname(tdbb, dscs[CRYPT_ARG_ALGORITHM], algorithmName); + + const unsigned ALG_RC4 = 1; + const unsigned ALG_CHACHA = 2; + const unsigned ALG_SOBER = 3; + static CodeValue algorithms[] = { + { ALG_RC4, "RC4" }, + { ALG_CHACHA, "CHACHA20" }, + { ALG_SOBER, "SOBER128" }, + { 0, nullptr } + }; + + CodeValue* a = nullptr; + string aName(algorithmName); + aName.lower(); + int cipher = find_cipher(aName.c_str()); + if (cipher < 0) + { + a = find(algorithms, algorithmName); + if (!a) + { + string error = "Unknown crypt algorithm "; + error += algorithmName.c_str(); + raise(error.c_str()); + } + } + + const unsigned MODE_ECB = 1; + const unsigned MODE_CBC = 2; + const unsigned MODE_CTR = 3; + const unsigned MODE_CFB = 4; + const unsigned MODE_OFB = 5; + static CodeValue modes[] = { + { MODE_ECB, "ECB" }, + { MODE_CBC, "CBC" }, + { MODE_CTR, "CTR" }, + { MODE_CFB, "CFB" }, + { MODE_OFB, "OFB" }, + { 0, nullptr } + }; + + CodeValue* m = nullptr; + MOV_get_metaname(tdbb, dscs[CRYPT_ARG_MODE], modeName); + if (cipher >= 0) + { + if (!modeName.hasData()) + raise("Should specify MODE parameter for symmetric algorithm"); + + m = find(modes, modeName); + if (!m) + raise("Unknown symmetric crypt mode"); + } + else if (modeName.hasData()) + raise("Mode parameter makes no sense for chosen algorithm"); + + unsigned len; + const void* data = CVT_get_bytes(dscs[CRYPT_ARG_KEY], len); + UCharBuffer key; + memcpy(key.getBuffer(len), data, len); + + UCharBuffer iv; + data = CVT_get_bytes(dscs[CRYPT_ARG_IV], len); + if ((m && (m->code != MODE_ECB)) || (a && (a->code != ALG_RC4))) // all other need IV + { + if (!len) + raise("Should specify initialization vector for chosen algorithm and/or mode"); + memcpy(iv.getBuffer(len), data, len); + } + else if (len) + raise("Initialization vector parameter makes no sense for chosen algorithm and/or mode"); + + const unsigned CTR_32 = 1; + const unsigned CTR_64 = 2; + const unsigned CTR_LITTLE_ENDIAN = 3; + const unsigned CTR_BIG_ENDIAN = 4; + + static CodeValue counterTypes[] = { + { CTR_LITTLE_ENDIAN, "CTR_LITTLE_ENDIAN" }, + { CTR_BIG_ENDIAN, "CTR_BIG_ENDIAN" }, + { 0, nullptr } + }; + + CodeValue *c = nullptr; + MOV_get_metaname(tdbb, dscs[CRYPT_ARG_CTRTYPE], counterType); + if (m && (m->code == MODE_CTR)) + { + if (counterType.hasData()) + { + c = find(counterTypes, counterType); + if (!c) + raise("Invalid counter type @1"); // counterType + } + else + c = &counterTypes[CTR_LITTLE_ENDIAN]; + } + else if (counterType.hasData()) + raise("Counter endianess parameter is not used in Mode @1 (m->name)"); + + FB_UINT64 ctrVal = 0; + if ((m && (m->code == MODE_CTR)) || (a && (a->code == ALG_CHACHA))) + { + if (dscs[CRYPT_ARG_COUNTER]->dsc_length) + { + ctrVal = MOV_get_int64(tdbb, dscs[CRYPT_ARG_COUNTER], 0); + if (m && ctrVal > key.getCount()) + raise("Too big value @1, maximum @2 can be used"); + } + } + else if (dscs[CRYPT_ARG_COUNTER]->dsc_length) + raise("@1 (counter length/value) parameter is not used in @1 (a ? a->name : 'Mode' m->name)"); + + // Run selected algorithm + DataPipe dp(tdbb, dscs[CRYPT_ARG_VALUE], impure); + if (m) + { + unsigned blockLen = cipher_descriptor[cipher].block_length; + if (iv.hasData() && iv.getCount() != blockLen) + raise("Invalid IV length @2, need @1"); // block_length, iv.getCount + + switch(m->code) + { + case MODE_ECB: + { + symmetric_ECB ecb; + tomCheck(ecb_start(cipher, key.begin(), key.getCount(), 0, &ecb), "initializing ECB mode"); + + while (dp.hasData()) + { + if (encryptFlag) + tomCheck(ecb_encrypt(dp.from(), dp.to(), dp.length(), &ecb), "encrypting in ECB mode"); + else + tomCheck(ecb_decrypt(dp.from(), dp.to(), dp.length(), &ecb), "decrypting in ECB mode"); + dp.next(); + } + ecb_done(&ecb); + } + break; + + case MODE_CBC: + { + symmetric_CBC cbc; + tomCheck(cbc_start(cipher, iv.begin(), key.begin(), key.getCount(), 0, &cbc), "initializing CBC mode"); + + while (dp.hasData()) + { + if (encryptFlag) + tomCheck(cbc_encrypt(dp.from(), dp.to(), dp.length(), &cbc), "encrypting in CBC mode"); + else + tomCheck(cbc_decrypt(dp.from(), dp.to(), dp.length(), &cbc), "decrypting in CBC mode"); + dp.next(); + } + cbc_done(&cbc); + } + break; + + case MODE_CFB: + { + symmetric_CFB cfb; + tomCheck(cfb_start(cipher, iv.begin(), key.begin(), key.getCount(), 0, &cfb), "initializing CFB mode"); + + while (dp.hasData()) + { + if (encryptFlag) + tomCheck(cfb_encrypt(dp.from(), dp.to(), dp.length(), &cfb), "encrypting in CFB mode"); + else + tomCheck(cfb_decrypt(dp.from(), dp.to(), dp.length(), &cfb), "decrypting in CFB mode"); + dp.next(); + } + cfb_done(&cfb); + } + break; + + case MODE_OFB: + { + symmetric_OFB ofb; + tomCheck(ofb_start(cipher, iv.begin(), key.begin(), key.getCount(), 0, &ofb), "initializing OFB mode"); + + while (dp.hasData()) + { + if (encryptFlag) + tomCheck(ofb_encrypt(dp.from(), dp.to(), dp.length(), &ofb), "encrypting in OFB mode"); + else + tomCheck(ofb_decrypt(dp.from(), dp.to(), dp.length(), &ofb), "decrypting in OFB mode"); + dp.next(); + } + ofb_done(&ofb); + } + break; + + case MODE_CTR: + { + symmetric_CTR ctr; + tomCheck(ctr_start(cipher, iv.begin(), key.begin(), key.getCount(), 0, + (c->code == CTR_LITTLE_ENDIAN ? CTR_COUNTER_LITTLE_ENDIAN : CTR_COUNTER_BIG_ENDIAN) | ctrVal, + &ctr), "initializing CTR mode"); + + while (dp.hasData()) + { + if (encryptFlag) + tomCheck(ctr_encrypt(dp.from(), dp.to(), dp.length(), &ctr), "encrypting in CTR mode"); + else + tomCheck(ctr_decrypt(dp.from(), dp.to(), dp.length(), &ctr), "decrypting in CTR mode"); + dp.next(); + } + ctr_done(&ctr); + } + break; + } + } + else + { + fb_assert(a); + switch(a->code) + { + case ALG_RC4: + { + rc4_state rc4; + tomCheck(rc4_stream_setup(&rc4, key.begin(), key.getCount()), "initializing RC4"); + + while (dp.hasData()) + { + tomCheck(rc4_stream_crypt(&rc4, dp.from(), dp.length(), dp.to()), + encryptFlag ? "encrypting RC4" : "decrypting RC4"); + dp.next(); + } + rc4_stream_done(&rc4); + } + break; + + case ALG_CHACHA: + { + chacha_state chacha; + tomCheck(chacha_setup(&chacha, key.begin(), key.getCount(), 20), "initializing CHACHA#20"); + switch(iv.getCount()) + { + case 12: + tomCheck(chacha_ivctr32(&chacha, iv.begin(), iv.getCount(), ctrVal), "setting IV for CHACHA#20"); + break; + case 8: + tomCheck(chacha_ivctr64(&chacha, iv.begin(), iv.getCount(), ctrVal), "setting IV for CHACHA#20"); + break; + default: + raise("Invalid IV length @2, need 8 or 12"); // iv.getCount + break; + } + + while (dp.hasData()) + { + tomCheck(chacha_crypt(&chacha, dp.from(), dp.length(), dp.to()), + encryptFlag ? "encrypting CHACHA#20" : "decrypting CHACHA#20"); + dp.next(); + } + chacha_done(&chacha); + } + break; + + case ALG_SOBER: + { + sober128_state sober128; + tomCheck(sober128_stream_setup(&sober128, key.begin(), key.getCount()), "initializing SOBER-128"); + tomCheck(sober128_stream_setiv(&sober128, iv.begin(), iv.getCount()), "setting IV for SOBER-128"); + + while (dp.hasData()) + { + tomCheck(sober128_stream_crypt(&sober128, dp.from(), dp.length(), dp.to()), + encryptFlag ? "encrypting SOBER-128" : "decrypting SOBER-128"); + dp.next(); + } + sober128_stream_done(&sober128); + } + break; + } + } + + return &impure->vlu_desc; +} + +dsc* evlEncrypt(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, + impure_value* impure) +{ + return evlEncryptDecrypt(tdbb, function, args, impure, true); +} + +dsc* evlDecrypt(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, + impure_value* impure) +{ + return evlEncryptDecrypt(tdbb, function, args, impure, false); +} + + +dsc* evlRsaEncryptDecrypt(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, + impure_value* impure, bool encryptFlag) +{ + tomcryptInitializer(); + + fb_assert(args.getCount() == RSA_CRYPT_ARG_MAX); + + jrd_req* request = tdbb->getRequest(); + + // parse args and check correctness + const dsc* dscs[RSA_CRYPT_ARG_MAX]; + for (unsigned i = 0; i < RSA_CRYPT_ARG_MAX; ++i) + dscs[i] = EVL_expr(tdbb, request, args[i]); + + MetaName hashName; + if (dscs[RSA_CRYPT_ARG_HASH]) + MOV_get_metaname(tdbb, dscs[RSA_CRYPT_ARG_HASH], hashName); + if (!hashName.hasData()) + hashName = "SHA256"; + string aName(hashName); + aName.lower(); + int hash = find_hash(aName.c_str()); + if (hash < 0) + { + string error = "Unknown hash algorithm "; + error += hashName.c_str(); + raise(error.c_str()); + } + + unsigned len; + const UCHAR* data = CVT_get_bytes(dscs[RSA_CRYPT_ARG_VALUE], len); + if (!data) + return nullptr; + + unsigned keyLen; + const UCHAR* key = CVT_get_bytes(dscs[RSA_CRYPT_ARG_KEY], keyLen); + if (!key) + return nullptr; + + unsigned paramLen; + const UCHAR* lParam = CVT_get_bytes(dscs[RSA_CRYPT_ARG_LPARAM], paramLen); + if (!paramLen) + lParam = nullptr; + + // Run tomcrypt functions + rsa_key rsaKey; + tomCheck(rsa_import(key, keyLen, &rsaKey), "importing RSA key"); + + unsigned long outlen = encryptFlag ? 256 : 190; + UCharBuffer outBuf; + int stat = 0; + int cryptRc = encryptFlag ? rsa_encrypt_key(data, len, outBuf.getBuffer(outlen), &outlen, lParam, paramLen, + prng().getState(), prng().getIndex(), hash, &rsaKey) : + rsa_decrypt_key(data, len, outBuf.getBuffer(outlen), &outlen, lParam, paramLen, hash, &stat, &rsaKey); + rsa_free(&rsaKey); + tomCheck(cryptRc, encryptFlag ? "RSA-encrypting" : "RSA-decrypting"); + if ((!encryptFlag) && (!stat)) + ERR_post(Arg::Gds(isc_random) << "Invalid OAEP packet"); + + dsc result; + result.makeText(outlen, ttype_binary, outBuf.begin()); + EVL_make_value(tdbb, &result, impure); + return &impure->vlu_desc; +} + +dsc* evlRsaDecrypt(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, impure_value* impure) +{ + return evlRsaEncryptDecrypt(tdbb, function, args, impure, false); +} + +dsc* evlRsaEncrypt(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, impure_value* impure) +{ + return evlRsaEncryptDecrypt(tdbb, function, args, impure, true); +} + +dsc* evlRsaPrivate(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, impure_value* impure) +{ + tomcryptInitializer(); + + fb_assert(args.getCount() == 1); + + jrd_req* request = tdbb->getRequest(); + + const dsc* value = EVL_expr(tdbb, request, args[0]); + if (request->req_flags & req_null) // return NULL if value is NULL + return NULL; + + const SLONG length = MOV_get_long(tdbb, value, 0); + if (length < 1 || length > 1024) + status_exception::raise(Arg::Gds(isc_arith_except) << Arg::Gds(isc_numeric_out_of_range)); + + rsa_key rsaKey; + tomCheck(rsa_make_key(prng().getState(), prng().getIndex(), length, 65537, &rsaKey), + "making RSA key"); + + unsigned long outlen = length * 16; + UCharBuffer key; + int cryptRc = rsa_export(key.getBuffer(outlen), &outlen, PK_PRIVATE, &rsaKey); + rsa_free(&rsaKey); + tomCheck(cryptRc, "exporting private RSA key"); + + dsc result; + result.makeText(outlen, ttype_binary, key.begin()); + EVL_make_value(tdbb, &result, impure); + return &impure->vlu_desc; +} + +dsc* evlRsaPublic(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, impure_value* impure) +{ + tomcryptInitializer(); + + fb_assert(args.getCount() == 1); + + jrd_req* request = tdbb->getRequest(); + + const dsc* value = EVL_expr(tdbb, request, args[0]); + if (request->req_flags & req_null) // return NULL if value is NULL + return NULL; + + unsigned len; + const UCHAR* data = CVT_get_bytes(value, len); + rsa_key rsaKey; + tomCheck(rsa_import(data, len, &rsaKey), "importing RSA key"); + + unsigned long outlen = len; + UCharBuffer key; + int cryptRc = rsa_export(key.getBuffer(outlen), &outlen, PK_PUBLIC, &rsaKey); + rsa_free(&rsaKey); + tomCheck(cryptRc, "exporting private RSA key"); + + dsc result; + result.makeText(outlen, ttype_binary, key.begin()); + EVL_make_value(tdbb, &result, impure); + return &impure->vlu_desc; +} + + +dsc* evlRsaSign(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, impure_value* impure) +{ + tomcryptInitializer(); + + fb_assert(args.getCount() == RSA_SIGN_ARG_MAX); + + jrd_req* request = tdbb->getRequest(); + + // parse args and check correctness + const dsc* dscs[RSA_SIGN_ARG_MAX]; + for (unsigned i = 0; i < RSA_SIGN_ARG_MAX; ++i) + dscs[i] = EVL_expr(tdbb, request, args[i]); + + MetaName hashName; + if (dscs[RSA_SIGN_ARG_HASH]) + MOV_get_metaname(tdbb, dscs[RSA_SIGN_ARG_HASH], hashName); + if (!hashName.hasData()) + hashName = "SHA256"; + string aName(hashName); + aName.lower(); + int hash = find_hash(aName.c_str()); + if (hash < 0) + { + string error = "Unknown hash algorithm "; + error += hashName.c_str(); + raise(error.c_str()); + } + + unsigned len; + const UCHAR* data = CVT_get_bytes(dscs[RSA_SIGN_ARG_VALUE], len); + if (!data) + return nullptr; + + SLONG saltLength = 8; + if (dscs[RSA_SIGN_ARG_SALTLEN]->dsc_length) + { + saltLength = MOV_get_long(tdbb, dscs[RSA_SIGN_ARG_SALTLEN], 0); + if (saltLength < 0 || saltLength > 32) + status_exception::raise(Arg::Gds(isc_arith_except) << Arg::Gds(isc_numeric_out_of_range)); + } + + unsigned keyLen; + const UCHAR* key = CVT_get_bytes(dscs[RSA_SIGN_ARG_KEY], keyLen); + if (!key) + return nullptr; + rsa_key rsaKey; + tomCheck(rsa_import(key, keyLen, &rsaKey), "importing RSA key"); + + unsigned long signLen = 1024; + UCharBuffer sign; + int cryptRc = rsa_sign_hash(data, len, sign.getBuffer(signLen), &signLen, + prng().getState(), prng().getIndex(), hash, saltLength, &rsaKey); + rsa_free(&rsaKey); + tomCheck(cryptRc, "RSA-signing data"); + + dsc result; + result.makeText(signLen, ttype_binary, sign.begin()); + EVL_make_value(tdbb, &result, impure); + return &impure->vlu_desc; +} + + +dsc* boolResult(thread_db* tdbb, impure_value* impure, bool value) +{ + dsc result; + FB_BOOLEAN rc = value ? FB_TRUE : FB_FALSE; + result.makeBoolean(&rc); + + EVL_make_value(tdbb, &result, impure); + return &impure->vlu_desc; +} + + +dsc* evlRsaVerify(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, impure_value* impure) +{ + tomcryptInitializer(); + + fb_assert(args.getCount() == RSA_VERIFY_ARG_MAX); + + jrd_req* request = tdbb->getRequest(); + + // parse args and check correctness + const dsc* dscs[RSA_VERIFY_ARG_MAX]; + for (unsigned i = 0; i < RSA_VERIFY_ARG_MAX; ++i) + dscs[i] = EVL_expr(tdbb, request, args[i]); + + MetaName hashName; + if (dscs[RSA_VERIFY_ARG_HASH]) + MOV_get_metaname(tdbb, dscs[RSA_VERIFY_ARG_HASH], hashName); + if (!hashName.hasData()) + hashName = "SHA256"; + string aName(hashName); + aName.lower(); + int hash = find_hash(aName.c_str()); + if (hash < 0) + { + string error = "Unknown hash algorithm "; + error += hashName.c_str(); + raise(error.c_str()); + } + + unsigned len; + const UCHAR* data = CVT_get_bytes(dscs[RSA_VERIFY_ARG_VALUE], len); + if (!data) + return nullptr; + + unsigned signLen; + const UCHAR* sign = CVT_get_bytes(dscs[RSA_VERIFY_ARG_SIGNATURE], signLen); + if (!sign) + return boolResult(tdbb, impure, false); + + SLONG saltLength = 8; + if (dscs[RSA_VERIFY_ARG_SALTLEN]->dsc_length) + { + saltLength = MOV_get_long(tdbb, dscs[RSA_VERIFY_ARG_SALTLEN], 0); + if (saltLength < 0 || saltLength > 32) + status_exception::raise(Arg::Gds(isc_arith_except) << Arg::Gds(isc_numeric_out_of_range)); + } + + unsigned keyLen; + const UCHAR* key = CVT_get_bytes(dscs[RSA_VERIFY_ARG_KEY], keyLen); + if (!key) + return boolResult(tdbb, impure, false); + rsa_key rsaKey; + tomCheck(rsa_import(key, keyLen, &rsaKey), "importing RSA key"); + + int state = 0; + int cryptRc = rsa_verify_hash(sign, signLen, data, len, hash, saltLength, &state, &rsaKey); + rsa_free(&rsaKey); + if (cryptRc != CRYPT_INVALID_PACKET) + tomCheck(cryptRc, "verifying RSA-signed data"); + else + state = 0; + + return boolResult(tdbb, impure, state); +} + dsc* evlDateDiff(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, impure_value* impure) @@ -4647,6 +5702,14 @@ const SysFunction SysFunction::functions[] = {"COT", 1, 1, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfCot}, {"DATEADD", 3, 3, setParamsDateAdd, makeDateAdd, evlDateAdd, NULL}, {"DATEDIFF", 3, 3, setParamsDateDiff, makeInt64Result, evlDateDiff, NULL}, + {"DECRYPT", 7, 7, setParamsEncrypt, makeDecrypt, evlDecrypt, NULL}, + {"ENCRYPT", 7, 7, setParamsEncrypt, makeEncrypt, evlEncrypt, NULL}, + {"RSA_DECRYPT", 4, 4, setParamsRsaEncrypt, makeRsaDecrypt, evlRsaDecrypt, NULL}, + {"RSA_ENCRYPT", 4, 4, setParamsRsaEncrypt, makeRsaEncrypt, evlRsaEncrypt, NULL}, + {"RSA_PRIVATE", 1, 1, setParamsInteger, makeRsaPrivate, evlRsaPrivate, NULL}, + {"RSA_PUBLIC", 1, 1, setParamsRsaPublic, makeRsaPublic, evlRsaPublic, NULL}, + {"RSA_SIGN", 4, 4, setParamsRsaSign, makeRsaSign, evlRsaSign, NULL}, + {"RSA_VERIFY", 5, 5, setParamsRsaVerify, makeBoolResult, evlRsaVerify, NULL}, {"EXP", 1, 1, setParamsDblDec, makeDblDecResult, evlExp, NULL}, {"FIRST_DAY", 2, 2, setParamsFirstLastDay, makeFirstLastDayResult, evlFirstLastDay, (void*) funFirstDay}, {"FLOOR", 1, 1, setParamsDblDec, makeCeilFloor, evlFloor, NULL}, diff --git a/src/jrd/dfw.epp b/src/jrd/dfw.epp index 0facac2a7d..81d79cce40 100644 --- a/src/jrd/dfw.epp +++ b/src/jrd/dfw.epp @@ -994,7 +994,7 @@ namespace Jrd::ContextPoolHolder context(tdbb, new_pool); const Firebird::MetaName depName(work->dfw_package.isEmpty() ? - work->dfw_name : work->dfw_package); + Firebird::MetaName(work->dfw_name) : work->dfw_package); MET_get_dependencies(tdbb, NULL, NULL, 0, NULL, &blobId, (compile ? &statement : NULL), NULL, depName, diff --git a/src/jrd/mov.cpp b/src/jrd/mov.cpp index 4564c0c434..ad15676f28 100644 --- a/src/jrd/mov.cpp +++ b/src/jrd/mov.cpp @@ -174,7 +174,7 @@ void MOV_get_metaname(Jrd::thread_db* tdbb, const dsc* desc, MetaName& name) const USHORT length = CVT_get_string_ptr(desc, &ttype, &ptr, NULL, 0, tdbb->getAttachment()->att_dec_status); - fb_assert(length && ptr); + fb_assert(ptr); fb_assert(length <= MAX_SQL_IDENTIFIER_LEN); fb_assert(ttype == ttype_ascii || ttype == ttype_metadata); diff --git a/src/yvalve/keywords.cpp b/src/yvalve/keywords.cpp index 46d387974a..dd11c572e0 100644 --- a/src/yvalve/keywords.cpp +++ b/src/yvalve/keywords.cpp @@ -140,11 +140,15 @@ static const TOK tokens[] = {TOK_COSH, "COSH", true}, {TOK_COT, "COT", true}, {TOK_COUNT, "COUNT", false}, + {TOK_COUNTER, "COUNTER", true}, {TOK_COVAR_POP, "COVAR_POP", false}, {TOK_COVAR_SAMP, "COVAR_SAMP", false}, {TOK_CREATE, "CREATE", false}, {TOK_CROSS, "CROSS", false}, {TOK_CSTRING, "CSTRING", true}, + {TOK_CTR_BIG_ENDIAN, "CTR_BIG_ENDIAN", true}, + {TOK_CTR_LENGTH, "CTR_LENGTH", true}, + {TOK_CTR_LITTLE_ENDIAN, "CTR_LITTLE_ENDIAN", true}, {TOK_CUME_DIST, "CUME_DIST", true}, {TOK_CURRENT, "CURRENT", false}, {TOK_CURRENT_CONNECTION, "CURRENT_CONNECTION", false}, @@ -247,6 +251,7 @@ static const TOK tokens[] = {TOK_INVOKER, "INVOKER", true}, {TOK_IS, "IS", false}, {TOK_ISOLATION, "ISOLATION", true}, + {TOK_IV, "IV", true}, {TOK_JOIN, "JOIN", false}, {TOK_KEY, "KEY", true}, {TOK_LAG, "LAG", true}, @@ -276,6 +281,7 @@ static const TOK tokens[] = {TOK_LONG, "LONG", false}, {TOK_LOWER, "LOWER", false}, {TOK_LPAD, "LPAD", true}, + {TOK_LPARAM, "LPARAM", true}, {TOK_MANUAL, "MANUAL", true}, {TOK_MAPPING, "MAPPING", true}, {TOK_MATCHED, "MATCHED", true}, @@ -290,6 +296,7 @@ static const TOK tokens[] = {TOK_MINUTE, "MINUTE", false}, {TOK_MINVALUE, "MINVALUE", true}, {TOK_MOD, "MOD", true}, + {TOK_MODE, "MODE", true}, {TOK_MODULE_NAME, "MODULE_NAME", true}, {TOK_MONTH, "MONTH", false}, {TOK_NAME, "NAME", true}, @@ -404,8 +411,16 @@ static const TOK tokens[] = {TOK_ROW_NUMBER, "ROW_NUMBER", true}, {TOK_ROWS, "ROWS", false}, {TOK_RPAD, "RPAD", true}, + {TOK_RSA_DECRYPT, "RSA_DECRYPT", true}, + {TOK_RSA_ENCRYPT, "RSA_ENCRYPT", true}, + {TOK_RSA_PRIVATE, "RSA_PRIVATE", true}, + {TOK_RSA_PUBLIC, "RSA_PUBLIC", true}, + {TOK_RSA_SIGN, "RSA_SIGN", true}, + {TOK_RSA_VERIFY, "RSA_VERIFY", true}, + {TOK_SALT_LENGTH, "SALT_LENGTH", true}, {TOK_SAVEPOINT, "SAVEPOINT", false}, {TOK_SCALAR_ARRAY, "SCALAR_ARRAY", true}, + {TOK_SIGNATURE, "SIGNATURE", true}, {TOK_DATABASE, "SCHEMA", false}, // Alias of DATABASE {TOK_SCROLL, "SCROLL", false}, {TOK_SECOND, "SECOND", false},