Re: [firebase-br] Segurança do firebird
Carlos H. Cantu (TeamFB)
listas em warmboot.com.br
Sex Dez 15 18:32:49 -03 2006
Para quem estiver interessado, segue a implementação sugerida pelo
Vlad:
0. Principles and concepts
- Cryptoplugin
The pluggable library realizing special programming interface
- PluginID
The unique identifier of cryptoplugin. Serves for distinguishing of
cryptoplugins
of different manufacturers in case of their simultaneous usage by
one engine.
May (should?) be differ for different versions of one cryptoplugin.
Determined by the manufacturer
- MethodID
The name of a encoding method. Serves for choice of an encoding
method for DB
(and key lengths?) if installed cryptoplugins support more than one
method.
Determined by the manufacturer, the good form considers usage of
common names
(DES, AES, BlowFish etc)
- KeyName
The name having sense for cryptoplugin. The cryptoplugin associates
with this name
storage of a key of authorization. Set by the user implicitly at
encoding DB, or
generated by an engine automatically
- KeyDB
Key for database encoding. Symmetric. Used for encoding and decoding
of
database data itself. Generated automatically and not stored in open
form
anywhere
- KeyAuth (KeyAuthOpen\KeyAuthClosed)
Pair of keys for authorization. Asymmetric. Used for encoding KeyDB
and for
authorization of applications. Generated automatically or by special
utility).
The open (public) part is accessible by an engine, closed (private) -
to the
user application or\and to the cryptoplugin but unknown to the engine
- KeySession (KeySessionOpen\KeySessionClosed)
Pair of keys used at authorization of the user application.
Asymmetric.
Generated automatically during application authorization phase for
safe
transfer of other keys
1. Creation of ciphered or encoding earlier not ciphered DB
<CREATE | ALTER> DATABASE ...
ENCRYPTED BY <[PluginID.]MethodID | DEFAULT> [USING KEY <KeyName>]
|
NOT ENCRYPTED
If PluginID and MethodID are both set then engine search among
installed
cryptoplugins the one with given parameters. If only MethodID is set the
first is selected suitable cryptoplugin, knowing given encoding method.
If DEFAULT is set cryptoplugin and the method is determined from
configuration
file (or used built-in if it is).
Optional KeyName sets a name of a key of authorization (KeyAuth) in
external
storage. The storage is completely determined by cryptoplugin. The given
name
allows to use a common authorization key for several DB. If the name does
not set,
it is generated by an engine (on the basis of a filename of a DB or
random ?)
NOT ENCRYPTED - decrypts a DB
If the DB is already ciphered, only NOT ENCRYPTED is allowed
The ALTER statement is accessible by SYSDBA and OWNER only
a) select cryptoplugin
b) cryptoplugin generates a key of encoding of a DB (KeyDB). Pair of keys
of
authorization (KeyAuthOpen \ KeyAuthClosed) searched using given (or
generated by an engine) KeyName. If such keys pair is not found it is
generated by cryptoplugin and saved in external storage under name
KeyName
c) encode KeyDB with the open part of an authorization key
KeyDB_enc = Encrypt (KeyDB, KeyAuthOpen)
d) save KeyDB_enc on header_page (or special page keys_page)
e) save PluginID, MethodID, KeyName and KeyAuthOpen on header_page as tag
of that
the DB is ciphered
f) header_page (and keys_page) are not ciphered
g) page headers (Ods:: pag) is not encoded, added the new page flag
indicating that
page is encoded. This flag have the same value to all page types
h) In case of ALTER DATABASE a DB encoded in a background (or onine -
during
execution time of ALTER DATABASE statement ? Whether it is necessary
exclusive
access to database ?)
2. Working with encoded database
CONNECT DATABASE ...
[AUTH[ORIZED] BY <auth_string>]
At the first opening of the DB file engine determined by header_page
if it is
ciphered and, if yes, searched PluginID.MethodID among installed
cryptoplugins.
There are three ways to transfer authorization key to the
cryptoplugin:
a) cryptoplugin searches for a key with the given name (KeyName) in
external storage
b) The key transfers the client application through DPB. If the
application does not
know which key is necessary for given DB it will not work
c) cryptoplugin initiates an callback to an engine, engine - to the user
application
for obtaining an authorization key from it. One of callback
parameters - KeyName
Thus necessity of authorization is defined by ability of cryptoplugin
to load an
authorization key by itself. Since the authorization key is saved by
cryptoplugin in
external storage at the moment of key generation authorization by the
application is
not required by default. If someone deletes an authorization key from the
storage
accessible to an instance of cryptoplugin, working in an engine, then
authorization
by application is required (of course the authorization key should be
accessible to
the cryptoplugin, working in the application)
The order of authorization of the user application, in case when it
is required
(callback during call of isc_attach_database):
a) engine: cryptoplugin generate session keys pair
(KeySessionOpen\KeySessionClosed)
b) engine: send to application KeyName, KeySessionOpen and KeyDB_enc
c) application: decoded a key of encoding of DB by the closed part of an
authorization
key and encoded its by open part of a session key:
KeyDB = Decrypt (KeyDB_enc, KeyAuthClosed)
KeyDB_enc2 = Encrypt (KeyDB, KeySessionOpen)
The authorization key should correspond to name KeyName
d) application: send back to an engine KeyDB_enc2
e) engine: decoded a key of encoding of DB by the closed part of a
session key
KeyDB = Decrypt (KeyDB_enc2, KeySessionClosed)
If there is a requirement that the user application should not know a
key of
encoding of DB it is possible to act a little differently:
a) engine: cryptoplugin generate session keys pair
(KeySessionOpen\KeySessionClosed)
b) engine: send to application KeyName and KeySessionOpen
c) application: finds KeyAuthClosed appropriate KeyName, and encoded its
by open
part of session key
KeyAuthClosed_enc = Encrypt (KeyAuthClosed, KeySessionOpen)
d) application: send back to an engine KeyAuthClosed_enc
e) engine decoded KeyAuthClosed and KeyDB :
KeyAuthClosed = Decrypt(KeyAuthClosed_enc, KeySessionClosed)
KeyDB = Decrypt(KeyDB_enc, KeyAuthClosed)
After obtaining KeyDB using an authorization key obtained from the
user application
(or from other source), engine checked up correctness of this KeyDB:
encoding obtained KeyDB with the open part of an authorization key:
KeyDB_enc = Encrypt (KeyDB, KeyAuthOpen)
and compare it with KeyDB_enc, stored on header_page (or keys_page)
The main purpose of such (complex) algorithm is: nobody should have
access to the
decrypted keys - nor user application, nor engine, nor network channel.
About engine's
access to the key content see below description of cryptocontainer
Note that the algorithm is not protected from interception and
substitution of keys by
the intermediary (man in the middle). So for the greater reliability it
is recommended to
encoding network traffic. Encoding of network traffic is out of the goals
of the given
proposition
3. Creation of encoded backup (gbak\nbackup)
Similar to creation of encoded database:
a) gbak uses the same PluginID.MethodID and KeyName as database or they
are set via
new command line switch. gbak should be able to work with cryptoplugin
b) cryptoplugin generates a new symmetric key for encoding backup (KeyBK)
and a pair of
authorization keys (KeyAuthOpen \ KeyAuthClosed) if KeyName was set
by the user.
If user not set KeyName gbak used open part of authorization key
(KeyAuthOpen) which
is on header_page. It is possible as for backup creation gbak needed
only the open part
of authorization key to encrypt KeyBK
c) save into external storage an authorization key (KeyAuthClosed) if it
was a new
generated key
d) encode KeyBK using an open part of an authorization key
KeyBK_enc = Encrypt (KeyBK, KeyAuthOpen)
e) save PluginID, MethodID, KeyName, KeyBK_enc and KeyAuthOpen in an open
part of
backup header
g) Other parts of backup is written on a disk in the encoded kind. If
used symmetric algorithm
worked with a blocks of data then last block is appended with a
special value till multiple
length of the algorithm's block size. At restore this value
distinguishes and ignored
4. Restore from encoded backup
Backup encoding key (KeyBK) determines similar to as at working with
encoded database
5. Cryptoplugin programming interface
The main principle used when this API was created - a database
encryption key should
not be accessible by the engine (firebird). Other used keys should not be
accessible whenever
possible. Also the engine should not be engaged in the task of encryption
methods, key lengths
or other parameters of encryption algorithms - all these details offered
to be coded in a name
of an encryption method (MethodID). This coding, certainly, is defined by
the cryptoplugin
manufacturer and documented by him.
Therefore the concept of the container of keys and (as in the
container may contain more
than one key simultaneously) descriptors of keys is introduced. The
container created by
cryptoplugin by name of encryption method, i.e. at working with the
container methods are
already known and it is not needed to be specified. Only container know
contents of an
encryption keys. Key leave container only encoded. There are two way for
key to leave
container: to export key into encoded in-memory representation (used for
key exchange)
and to save key into external storage under some name. Accordingly there
are ways to
import encoded key into container and to load key from external storage.
Encrypt and Decrypt operations, used in first parts of this
proposition, in fact are
export and import operations of cryptocontainer
The imported key completely defines the method of encryption
connected to it. I.e. when
exporting a key it is necessary to supply information about cryptoplugin
and a encryption method
(PluginID and MethodID). Is supposed a creation of the own copy of the
container for every
connection since contents of containers up to the moment of successful
authorization of the user
application are not identical. After successful authorization of the user
application the container
is destroyed, or if it was the first connection, associated with Database
and is used for encryption
of database pages. Accordingly the symmetric algorithm of encryption must
be threadsafe
Cryptoplugin have only one exported function (getCryptoEngine),
returning the instance of a
metaclass (CryptoEngine), representing actually cryptoplugin.
typedef KEY int;
// cryptoplugin's entrypoint
CryptoEngine* getCryptoEngine();
class CryptoEngine
{
public:
// returns unique string - indentifier of this cryptoplugin
const char* getEngineID() = 0;
// creates container implemented given methodID
// or returns 0 if methodID not supported
// container_name used by container to identify 'main' key when
save\load it
// into\from external location
CryptoContainer* createContainer(const char* methodID, const char*
container_name) = 0;
// destructor
void release() = 0;
}
// implements two encription methods - one symmetric and
// one asymmetric
class CryptoContainer
{
public:
enum KeyKind { kkSymmetric, kkASymmetric };
enum ExportMode { emOpenKey, emClosedKey };
// destructor
void release() = 0;
// generate key and fill in key descriptor
ISC_STATUS generateKey(KEY* key, KeyKind kind) = 0;
// encrypt open or closed part of key_exp by key_enc
// if buffer is NULL calculate length of exported key
ISC_STATUS exportKey(KEY key_exp, KEY key_enc, ExportMode mode,
char* buffer, int* len) = 0;
// take buffer, decrypt it by key_dec and return its
// descriptor as key_imp
ISC_STATUS importKey(KEY *key_imp, KEY key_dec, ExportMode mode,
char* buffer, int len) = 0;
// next two methods worked with 'external' key representation - it can
be
// file, registry key, record in some database etc.
// file name (registry key name etc) must be evaluated from
'container_name'
// parameter passed into container constructor
// external representation must have whole info to reconstruct the key
and
// encryption method i.e. it must contain both open and closed parts of
// assymetric key, pluginID and methodID to match key against container
// during key loading
// only one key can exists under given name in external location
// save key into external location known to this container
ISC_STATUS saveKey(KEY key_exp) = 0;
// load key from external location known to this container
ISC_STATUS loadKey(KEY* key_imp) = 0;
// erase internal key content and free allocated resources
ISC_STATUS eraseKey(KEY key) = 0;
// returns block size used by encryption method
int getBlockSize(KEY key) = 0;
ISC_STATUS encrypt(KEY key, char* in_buffer, int len, char* out_buffer)
= 0;
ISC_STATUS decrypt(KEY key, char* in_buffer, int len, char* out_buffer)
= 0;
}
All of the methods returns ISC_STATUS i.e. numeric error code. We can
create new FACILITY for this codes. Or we can use another approach - what
we used in ExternalEngine : in each method passed special object and
error
codes and string messages passed to the engine via methods of this object
Vlad
Mais detalhes sobre a lista de discussão lista