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