Skip to content

Latest commit

 

History

History
645 lines (503 loc) · 35.2 KB

FILTER.md

File metadata and controls

645 lines (503 loc) · 35.2 KB

Caml Crush: an OCaml PKCS#11 filtering proxy

PKCS#11 proxy filter module details

The following documentation is about the PKCS#11 filter logics and the filtering rules syntax.

The PKCS#11 filter is written in OCaml, and the rules syntax also use OCaml style expressions (the configuration module uses the OCaml config-file package).

Back to INDEX.md.

Disclaimer: on the filter possible side effects

Though the filter has been designed to be as transparent as possible regarding the PKCS#11 standard as well as the underlying PKCS#11 middleware, some side effects might appear due to the filtering implementation choices. If you use PKCS#11 conforming tests with ot without the filter (for example with the --without-filter option in the configure script), you will most likely see some minor differences.

This is mainly due to the fact that some PKCS#11 functions in the filter use local caches to check mechanisms and objects when they are listed. This can interfere with what the genuine underlying middleware would normally respond, especially when PKCS#11 tricky cases are tested (trying to use non existant mechanisms, ...).

However, one should keep in mind that in "normal" cases (meaning with standard PKCS#11 applications), the filter should not introduce glitches. If it does, please report the issue.

The filter architecture

         ----------------------  
        |  PKCS#11 RPC server  |
         ----------------------                   The PKCS#11 filter  
                   |                                 source tree
              --------------          _
             | 1] FRONTEND  |          |--- 1] /src/filter/fontend/frontend.ml
     ------------------------------    |
    |      PKCS#11 filter          |   |--- /src/filter/filter 
    |------------------------------|   |             [2] |- filter_common.ml
    |  --------------------------  |   |             [3] |- filter_configuration.ml
    | | user defined extensions  | |   |             [4] |- filter.ml
    | |            [5]           | |   |             [5] |- filter_actions.ml
    |  --------------------------  |   |             
    | | common  | parse  | core  | |   | 
    | | helpers | config | engine| |   |
    | |  [2]    |  [3]   |  [4]  | |   |
     ------------------------------    |
             | 6] BACKEND  |          _|--- 6] /src/filter/backend/backend.ml
              -------------            
                   |             
         ----------------------  
        |    PKCS#11 OCaml     | 
        |       bindings       | 
         ----------------------  
                   |
         { PKCS#11 INTERFACE }
                   |
         REAL PKCS#11 MIDDLEWARE
            (shared library)

The filter has a modular design in the project, and is composed of four main parts:

1] Frontend: this module is an isolation layer between the filter and the proxy server. It mainly consists in passthrough PKCS#11 calls.

2] Common helpers: the helpers define common logging and exceptions.

3] Configuration parser: the parser is in charge of reading the filter configuration file using config-file engine and rules. Some sanity checks are performed on the options.

4] Filter core engine: this is the main filtering engine, where the rules are enforced. It mainly consists in passthrough PKCS#11 calls for all the functions, with "hook" calls to filtering routines positionned where necessary.

5] User defined extensions: this module contains user defined extensions that will be called on some triggers during the filtering phase. It can be seen as a "plugins" system allowing custom routines to be applied in the filtering chain (with some restrictions though, see below).

6] Backend: this module is an isolation layer between the filter and the OCaml/C PKCS#11 bindings that talk to the "real" PKCS#11 library. It mainly consists in passthrough PKCS#11 calls.

Please note that the Frontend and Backend modules main purpose is to make it very easy to change the filter without touching to the other parts of the project. Changing the filter core is as easy as writing functions using an API conforming to what the Fontend/Backend expose.

The filter options syntax

The filter configuration is parsed through the OCaml config-file module. It uses OCaml style strings, integers, lists of tuples and regular expressions.

The accepted options keywords, with their OCaml style syntax, are:

  • debug = integer between 0 and 3
  • wrapping_format_key = a 32 char long string that must use hexadecimal values to setup the wrapping format key
  • modules = [(a1, b1), (a2, b2) ...] is a list of couples of strings (a, b) with 'a' being an alias, and 'b' being a PATH to the aliased PKCS#11 module
  • log_subchannel = string representing the filter log subchannel in the server
  • forbidden_mechanisms = [(a1, b1), (a2, b2) ...] is a list of couples where 'a' is a regular expression string representing modules and 'b' is a list of PKCS#11 mechanisms with the PKCS#11 definition syntax (CKM_RSA_PKCS for instance)
  • allowed_labels = [(a1, b1), (a2, b2) ...] is a list of couples where 'a1', 'a2', ... are regular expression strings representing module names, and 'b1', 'b2', ... are regular expressions representing labels
  • allowed_ids = [(a1, b1), (a2, b2) ...] is a list of couples where 'a1', 'a2', ... are regular expression strings representing module names, and 'b1', 'b2', ... are regular expressions representing ids
  • forbidden_functions = [(a1, b1), (a2, b2) ...] is a list of couples where 'a1', 'a2', ... are regular expression strings representing module names, and 'b1', 'b2', ... are lists of PKCS#11 functions with the PKCS#11 naming convention (C_Login, C_Logout ...)
  • enforce_ro_sessions = [(a1, b1), (a2, b2) ...] is a list of couples where 'a' is a regular expression string representing module names, and 'b1', 'b2', ... are booleans that can take 'true', 'false', 'yes' and 'no' as possible values
  • forbid_admin_operations = [(a1, b1), (a2, b2) ...] is a list of couples where 'a' is a regular expression string representing module names, and 'b1', 'b2', ... are booleans that can take 'true', 'false', 'yes' and 'no' as possible values
  • remove_padding_oracles = [(a1, b1), (a2, b2) ...] is a list of couples where 'a' is a regular expression string representing module names, and 'b1', 'b2', ... are a lists of cryptographic operations type that can take as possible values 'wrap', 'unwrap', 'encrypt', 'sign' and 'all' (this last one represents the sum of all the values)
  • filter_actions_pre = [(a1, b1), (a2, b2) ...] is a list of couples where 'a' is a regular expression string representing module names, and 'b1', 'b2', ... are lists of couples (c, d) where 'c' is a PKCS#11 function following the PKCS#11 naming convention (C_Login, C_Logout ...) and 'd' is an OCaml function name defined in /src/filter/filter/filter_actions.ml (it is a user defined action to be triggered when the PKCS#11 function 'c' is called as a 'pre action', see below for more details)
  • filter_actions_post = [(a1, b1), (a2, b2) ...] is a list of couples where 'a' is a regular expression string representing module names, and 'b1', 'b2', ... are lists of couples (c, d) where 'c' is a PKCS#11 function following the PKCS#11 naming convention (C_Login, C_Logout ...) and 'd' is an OCaml function name defined in /src/filter/filter/filter_actions.ml (it is a user defined action to be triggered when the PKCS#11 function 'c' is called as a 'post action', see below for more details)

The meaning of each key word is detailed in the following sections.

Logging and debugging options

  • debug (integer) can be used to increase the verbosity level:
    • 0 = merely no log at all, except critical errors and printing the debug level itself
    • 1 = level 0 + positive filtering matches (i.e. when the filter detects something to block)
    • 2 = level 1 + negative filtering matches (i.e. when the filter detects that it must not block something)
    • 3 = level 2 + print all the fetched configuration variables in the filter configuration file (modules aliasing, filtered labels, filtered ids, ...)

default is debug = 0

Syntax example:

debug = 3
  • log_subchanel (string):
    • Netplex allows the use of subchannels to split the logging file. You can specify a subchannel (also defined in pkcs11proxyd.conf) to send the log stream from the filter to an alternative output.

default is log_subchanel = the main pkcs11proxyd server channel

Syntax example:

log_subchanel = mylogsubchannel

Please note that the subchannel must indeed exist in the Netplex server context (meaning that it has been declared in the server configuration file). If this is not the case, the filter logs will fallback to the standard Netplex server output.

Wrapping format key

  • wrapping_format_key (32 char long string) can be used to configure the AES-128 bit key use as wrapping format key.

No default value is provided, you MUST uncomment and use a cryptographically sound random values when using the wrapping_format_patch function of the patchset 1 which is the default configuration. It must be converted to hexadecimal format.

PKCS#11 modules options

  • modules (list of couples of [string_regexp x string_path_to_lib]):
    • As mentionned previously, the client asks for a specific module name. The "modules" parameter binds module "names" and the path to the corresponding PKCS#11 library.

This option is mandatory: if no module is defined, an error is triggered.

Syntax example:

modules = [ ("softhsm", "/usr/lib/libsofthsm.so"), 
            ("opensc", "/usr/lib/opensc-pkcs11.so") ]

This will alias "softhsm" to the library "/usr/lib/libsofthsm.so", and "opensc" to "/usr/lib/opensc-pkcs11.so". Please note that you can also use an empty string "" as an alias. On the client side, the compiled client library libp11clientsofthsm.so will send the string "softhsm", while libp11client.so will send the empty string (the alias sent by the client library is hardcoded inside the binary at compilation time).

Filtering PKCS#11 objects options

The following two options are used to filter what objects the client application will be able to manipulate. It uses the PKCS#11 CKA_LABEL and CKA_ID attributes to filter objects.

The purpose is to define a list of objects you want to match for each module regarding their label and/or id, objects not matching will not be visible to the application.

  • allowed_labels (list of couples of [string_regexp x list of string_regexp]):
    • The allowed CKA_LABEL labels for the defined modules

There is no default value for this option: if no allowed_label option is defined, there is no filtering enforced on labels. The modules aliases regular expressions must however match an existing alias defined in the modules option: an error is triggered if this is not the case.

Syntax example:

allowed_labels = [ ("\\(sofths.*\\|opencryptoki\\)", ["MYLABEL.*", "LABEL_EXACT"]), 
                   ("softhsm", [".*THE_LABEL", "mytes.*"]) ]

The first rule of the above example will allow any "MYLABEL.*" regular expression, or a "LABEL_EXACT" for any module alias matching the "(sofths.*|opencryptoki)" regular expression (please note the escaping characters for regexps). For example, an object in a module related to the alias "softhsm" with a "MYLABEL1" label is allowed, but would not be allowed with a "1MYLABEL" label. Following the same rule, an object in a module related to the alias "softhsm111" with a label "MYLABEL" is allowed. If a module alias is not covered at all by any regexp rule in allowed_labels, it is not filtered. Finally, whenever a module alias is conerned by more than one rule, a logical OR is applied on the filtered labels: the "softhsm" alias accepts objects with the regexps "MYLABEL.*" OR "LABEL_EXACT" OR ".*THE_LABEL" OR "mytes.*" in the previous example.

  • allowed_ids (list of couples of [string_regexp x list of string_regexp]):
    • The allowed CKA_ID values for the defined modules (these are hexadecimal encoded values, since the ID is generally in a raw binary format)

Syntax example:

allowed_ids = [ ("softhsm.*", ["0123.*"]) ]

The allowed_ids filtering patterns follow the same rules as the one explained in the allowed_labels section.

Filtering PKCS#11 mechanisms options

This section describe the filtering of PKCS#11 mechanisms. The two options can be used to restrict the mechanisms that will be available to client applications and block some known attacks that use the properties of bad encryption padding to perform padding oracle attaks (with PKCS#1 v1.5 or CBC paddings for instance) or Wrap/Decrypt style attacks. These attacks are inherent to the PKCS#11 standard, and preventing the usage of the dangerous associated mechanisms will inhibit them (though it might be a too coarse and limiting solution, since the Wrap/Unwrap mechanism might be necessary in some use cases). A fine grained approach to prevent these attacks would require a "stateful" filter (i.e. memorizing the past PKCS#11 calls and conditionnally decide whether to filter a call or not): this is not the case of the current filter, but there is some work in progress regarding this issue.

  • forbidden_mechanisms (list of couples of [string_regexp x list of PKCS#11_mechanism]):
    • This option sets up a black list of forbidden PKCS#11 mechanisms using the PKCS#11 syntax. Whenever a client lists the mechanisms of a token, these mechanisms are transparently removed from the "real" mechanisms list exposed by the "real" PKCS#11 module. In addition, if the client tries to use these mechanisms through any of the cryptographic "Init" functions (C_Encrypt_Init, C_Decrypt_Init, ...), the filter blocks the call with a CKR_MECHANISM_INVALID. Each couple of the list contains a module alias regexp as a first element, and a list of mechanisms as a second element.

Syntax example:

forbidden_mechanisms = [ ("sof.*", [CKM_RSA_PKCS, CKM_MD5_RSA_PKCS]), 
                         ("softhsm", [CKM_DES_ECB]) ]

There is no default value for forbidden_mechanisms. Please note that the mechanisms are not regexps, they must correspond to exact PKCS#11 mechanism names as they are listed in the standard. If a module alias name is covered by two rules, a logical AND is applied: the rule given in the previous example will inhibit CKM_RSA_PKCS AND CKM_MD5_RSA_PKCS AND CKM_DES_ECB.

  • remove_padding_oracles (list of couples of [string_regexp x list of (wrap|unwrap|encrypt|sign|all)]):
    • This option blocks all the mechanisms that are considered dangerous because they can introduce a padding oracle. For now, such mechanisms are hardcoded in the filter (see their list below). For these mechanisms, one can define
      if a wrap, unwrap, encrypt, sign is forbidden: the operations will be forbidden for all the mechanisms. The special value all is a short key word for "wrap and unwrap and encrypt and sign". These rules are implemented in the filter by blocking C_Wrap, C_Unwrap, C_Encrypt_Init, C_Sign_Init for the dangerous mechanisms, with a CKR_MECHANISM_INVALID as error value. One should notice that remove_padding_oracles overlaps the forbidden_mechanisms option, as well as the forbidden_functions option (see below for a detailed description of this option). However, we find it more straightforward to give the user the opportunity to easily block known PKCS#11 weaknesses.

The potentially dangerous mechanisms (with PKCS#1 v1.5 or CBC paddings) that are harcoded inside the filter are:

[CKM_RSA_PKCS; CKM_MD2_RSA_PKCS; CKM_MD5_RSA_PKCS; CKM_SHA1_RSA_PKCS; CKM_RIPEMD128_RSA_PKCS; 
 CKM_RIPEMD160_RSA_PKCS; CKM_SHA256_RSA_PKCS; CKM_SHA384_RSA_PKCS; CKM_SHA512_RSA_PKCS; CKM_RC2_CBC_PAD; 
 CKM_DES_CBC_PAD; CKM_DES3_CBC_PAD; CKM_CDMF_CBC_PAD; CKM_CAST_CBC_PAD; CKM_CAST3_CBC_PAD; 
 CKM_CAST5_CBC_PAD; CKM_CAST128_CBC_PAD; CKM_RC5_CBC_PAD; CKM_IDEA_CBC_PAD; CKM_AES_CBC_PAD; 
 CKM_RSA_X_509]

Syntax example:

remove_padding_oracles = [ (".*", [wrap, unwrap, encrypt]), 
                           ("softhsm", [sign]) ]

There is no default value for remove_padding_oracles. As for the forbidden_mechanisms option, a logical AND is applied when a module is covered by different rules: the "softhsm" module will have "wrap", "unwrap" and "encrypt" forbidden because of the first rule, and "sign" forbidden with the second rule.

Filtering PKCS#11 functions options

  • forbidden_functions (list of couples of [string_regexp x list of PKCS#11_function]):
    • This option blocks any PKCS#11 function defined in the standard by returning CKR_FUNCTION_NOT_SUPPORTED. The PKCS#11 function names must exactly correspond to the ones used in the standard API, such as C_Login, C_Wrap, ...

Syntax example:

forbidden_functions  = [ ("soft.*", [C_Login, C_Logout]), 
                         ("softhsm", [C_Sign]) ]

There is no default value for forbidden_functions. As for the forbidden_mechanisms option, a logical AND is applied when a module is covered by different rules: the "softhsm" module will have C_Login and C_Logout blocked by the first rule, and C_Sign blocked by the second rule.

Filtering sessions options

  • enforce_ro_sessions (list of couples of [string_regexp x boolean]):
    • This option will enforce all the sessions to be Read Only, even if the user positions the RW flag when opening them. This option will preserve the token objects against any modification, and can be useful when the user is only intented to use the token as a cryptographic ressource with existing objects that an "administrator" has provisionned in the token. As for many other options, the RO sessions can be enforced per module alias. The possible values to express the boolean decision of enforcing or not RO sessions are: yes and no or true and false.

Syntax example:

enforce_ro_sessions  = [ ("soft.*", no), 
                         ("opencryptoki", yes) ]

The default value for enforce_ro_sessions is "no" (meaning that if there is no rule associated to a module, the RO sessions are not enforced). The previous rule will not enforce RO sessions for "softhsm", but will enforce them for the opencryptoki module alias.

  • forbid_admin_operations (list of couples of [string_regexp x boolean]):
    • This option will block administration operations on the token associated to a module alias by refusing any SO (Security Officer) C_Login. The purpose is to prevent "normal" users to perform administrative tasks on the tokens. Please note that the PKCS#11 way of segregating normal users and SO users is to use two different PINs. However, if the SO PIN can be bruteforced (for example if there is no bad PIN counter as this can be the case on some Hardware Security Modules), a normal user would be able to perform SO operations by guessing the SO PIN. This filter option can be seen as a "barrier" blocking such attacks whenever one is sure that a token has no reason to be administrated. The possible values to express the boolean decision of enforcing or not admin blocking are: yes and no or true and false.

Syntax example:

forbid_admin_operations = [ (".*", yes) ]

The default value for forbid_admin_operations is "no" (meaning that if there is no rule associated to a module, the SO operations are allowed). The previous rule enforces blocking the SO operations on all the modules (thanks to the regexp ".*" matching all the modules).

Adding user defined actions

The filter_actions option syntax and usage

  • filter_actions (list of couples of [string_regexp x list of couples of [PKCS#11_function x custom_function]]):
    • This option is a way to extend the filter features as the user can provide its own hooks on every PKCS#11 function. In order to apply an action "Action" triggered by a call to a PKCS#11 function, say C_Login for example, a couple (C_Login, Action) is defined in the filter_actions option. For the sake of simplicity, these hooks have been gathered inside one file in the filter source tree src/filter/filter/filter_actions.ml.

Syntax example:

filter_actions = [ (".*", [(C_Login, c_Login_hook), (C_Initialize, c_Initialize_hook)]),
                   ("soft.*", [(C_CloseSession, identity), (C_Login, c_Login_hook2)]) ]

There is no default value for filter_actions: if no rule is defined for a given PKCS#11 function, no hook will be executed for this function. If many rules concern the same PKCS#11 function, the hooks are executed in the order they are declared. The previous rule will execute the user defined c_Login_hook and then c_Login_hook2 when C_Login is called for all the modules, the identity user defined function when C_CloseSession is called for "soft.*" regexp module aliases ("softhsm" for instance), and so on ... Please beware that the user defined hooks are executed prior to any other filtering rule. In addition, depending on the hooking function return value, the other filtering rule might or might not be enforced: this is a way to override the original filtering rules and replace them with custom ones (see below for details on how this works).

Adding a new user defined action in the code

In order to add a new defined action, the user must edit the src/filter/filter/filter_actions.ml file where there are already some very simple examples of hooking functions:

  • identity is designed to hook pretty much anything: it prints " ######### Identity hook called!"
  • c_Initialize_hook is designed to hook C_Initialize, it prints the " ########## Hooking C_Initialize!" string at log level 1
  • c_Login_hook is designed to hook C_Login, it prints the " ######### Passthrough C_Login with pin %s!" string with the C_Login given PIN. If PIN is "1234", the hooks returns and lets C_Login continue its normal execution. If PIN is not "1234", C_Login is interrupted and the PKCS#11 error CKR_PIN_LOCKED is returned. Though this action is kind of useless, it shows the main advantage of user defined routines: one can completely customize the filter since input and output values can be handled here. One can also make "real" PKCS#11 calls to the Backend and decide of the filtering action depending on the result.

The PKCS#11 functions hooking system uses the OCaml marshaling module. The user defined functions must take exactly one argument that corresponds to the marshaled string of the original PKCS#11 function original arguments tuple (this argument is therefore of type string). Similarly, the output values of custom hooks are strings that are the marshaled versions of couples whose first element is a boolean value, and the second element is a PKCS#11 return value. If the first element is "false", then the hooked PKCS#11 function will continue its execution after the hook execution, ignoring the second element of the couple. This means that in this case, all the other filtering options are applied after the hook execution. On the contrary, if the first element of the couple is "true", the second element of the couple is considered to be the hooked function return value: this means that the hooked PKCS#11 function will return with this value just after the hook execution.

If more than one hooking routine are defined for the same PKCS#11 function, the hooks are executed in the order they are defined inside the filter_actions option. In this case, the first hooking routine that returns something with (true, ...) will stop the other hooks execution and makes the hooked function return with this value. This means that the other hooks and their return values are discarded.

If a "state" is necessary to keep track of actions of different hooks on the same PKCS#11 function, one will have to implement it through global variables for instance.

Two kind of user defined actions have been implemented:

  • Early actions: they are defined through the filter_actions_pre option. They are called before any filtering action at the very beginning of each hooked PKCS#11 function. This means that a user can, through early actions, completely replace the filter action on any given function with his defined actions, thus bypassing the genuine core engine process.
  • Late actions: they are defined through the filter_actions_post option. They are called at the end of filtering actions, generally just before the real call to the backend. This means that other filtering actions (such as functions blocking, label and id filtering ...) have been processed when these user defined actions are executed. Hence, late actions are meant to define actions extending (i.e. complementing and 'living with') the actions that are already performed in the filter core engine.

Code example

In order to add a custom hook, say c_Login_hook, one must first add the name of the hook in the two custom action wrappers defined in src/filter/filter/filter_actions.ml:

(********* CUSTOM actions wrappers for the configuration file ******)
let execute_action action argument = match action with
  "c_Initialize_hook" -> c_Initialize_hook argument
| "c_Login_hook" -> c_Login_hook argument
| "identity" -> identity argument
| _ -> identity argument

let string_check_action a = match a with
  "c_Initialize_hook" -> a
| "c_Login_hook" -> a
| "identity" -> a
| _ -> let error_string = Printf.sprintf "Error: unknown action option '%s'!" a in 
                          netplex_log_critical error_string; raise Config_file_wrong_type

Then, the user must define the c_Login_hook above these custom wrappers so that it is define at this point of the source file. One could also add the custom hooks inside another OCaml module that would be included in filter_actions.ml.

let c_Login_hook arg =
  let (cksessionhandlet_, ckusertypet_, pin) = (deserialize arg) in
  if compare (Pkcs11.byte_array_to_string pin) "1234" = 0 then
    (* Passtrhough if pin is 1234 *)
    let s = Printf.sprintf " ######### Passthrough C_Login with pin %s!" 
            (Pkcs11.byte_array_to_string pin) in print_debug s 1;
    (serialize (false, ()))
  else
  begin
    (* Hook the call if pin != 1234 *)
    let s = Printf.sprintf " ######### Hooking C_Login with pin %s!" 
            (Pkcs11.byte_array_to_string pin) in print_debug s 1;
    let return_value = serialize (true, Pkcs11.cKR_PIN_LOCKED) in
    (return_value)
  end

Here are the important parts to notice for c_Login_hook:

  • It has exactly one argument arg that is unmarshaled through the deserialize function
  • The unmarshaled arg is then affected to the tuple (cksessionhandlet_, ckusertypet_, pin) whose elements exactly correspond to what the hooked PKCS#11 function C_Login expects as arguments. These are the raw (meaning untouched) arguments received from the filter Frontend
  • Then, depending on the value of the PIN, either the couple (false, ()) or the couple (true, Pkcs11.cKR_PIN_LOCKED) are returned:
    • The couple (false, ()) means that we do not want to override C_Login return value: the PKCS#11 function will continue its execution and execute other elements of the filter
    • The couple (true, Pkcs11.cKR_PIN_LOCKED) means that we want C_Login to stop its execution at the hook call while returning Pkcs11.cKR_PIN_LOCKED

The user defined actions limitations

Though the custom hooks system has been designed to be very flexible, its main issues come from this flexibility. The OCaml marshaling module is indeed very powerful since it provides an easy way to define general purpose functions where the arguments and return values are evaluated at runtime. The two drawbacks are that:

  • The default OCaml marshaling module is not type-safe since no type is carried with the marshaled data, meaning that no type-checking is performed during the unmarshaling. This can lead to uncaught exceptions during the unmarshal.
  • The user defined functions input and output values must be handled with care: there is no safety net if the user fails to properly write his function. The program might compile, but the function will - eventually silently - fail at runtime since it overrides OCaml's type inference at compile time. This is usually not the expected behaviour for OCaml written programs!

Improving the user defined hooks to be type-safe and avoid the use of marshaling is a work in pogress.

Advanced examples of user defined actions

In order to illustrate the flexibility of the user extension system implemented in the filter, we provide patches that fix the PKCS#11 API. The API has been deeply analyzed during the last few years, leading to a formal model, an automated attack tool Tookan as well as a patched token reference implementation with CryptokiX.

Bortolozzo et al., in their ACM CCS 2010 paper, give a good overview of why PKCS#11 is not safe as is and how to properly fix it regarding their attacker model: see here for more details on this.

We provide in src/filter/filter/p11fix_patches patches that should enhance the security of the existing middlewares by using, among other patches, those implemented in CryptokiX. These patches are defined as filter_actions_post functions since we want them to live with the other filter actions. See below for detailed description of their action.

Please note that these patches are still in "beta test": they might evolve/be fixed in the future. They have only been tested with OpenCryptoki, but we plan to extend this soon.

Detail of included patchset

The default filter.conf ships with secure by default configuration. However, depending on some use case, relaxing the default rules might be necessary. Remember, patchset 1 and 2 are incompatible. The following will explain what is the effect of function applied in the patchsets.

  • do_segregate_usage, default OFF, patchset 1/2, applies to:
    • C_Initialize

This patch ensures that key usage segregation is enforced (encrypt/decrypt versus sign/verify). This is valid for object creation but also for existing objects.

  • non_local_objects_patch, patchset 1, applies to:
    • C_CreateObject
    • C_CopyObject
    • C_SetAttributeValue

When using the CryptokiX patches, we want to avoid keys created through C_CreateObject to circumvent the protections. Hence, we filter C_CreateObject and do not allow WRAP/UNWRAP attributes set with C_SetAttributeValue/C_CopyObject for non local objects - i.e. CKA_LOCAL=FALSE.

  • prevent_sensitive_leak_patch, patchset 1/2, applies to:
    • C_GetAttributeValue
    • C_SetAttributeValue

This patch prevents directly reading or writhing to sensitive or extractable keys. This patch also prevents directly setting CKA_ALWAYS_SENSITIVE and CKA_NEVER_EXTRACTABLE.

  • conflicting_attributes_patch, patchset 1, applies to:
    • C_CreateObject
    • C_CopyObject
    • C_UnwrapKey
    • C_GenerateKey
    • C_GenerateKeyPair
    • C_DeriveKey
    • C_SetAttributeValue

This patch prevents creating objects with conflicting attributes such as CKA_WRAP and CKA_DECRYPT.

  • conflicting_attributes_patch_on_existing_objects, patchset 1/2, applies to:
    • C_EncryptInit
    • C_DecryptInit
    • C_SignInit
    • C_SignRecoverInit
    • C_VerifyInit
    • C_VerifyRecoverInit
    • C_DeriveKey
    • C_DigestKey
    • C_WrapKey
    • C_UnwrapKey
    • C_FindObjects

This patch prevents using objects with have conflicting attributes. This allows to use a device although insecure objects are stored on it.

  • dangerous_sensitive_keys_paranoid/dangerous_sensitive_keys_escrow_encrypt/dangerous_sensitive_keys_escrow_all, patchset 1/2, applies to:
    • C_EncryptInit
    • C_DecryptInit
    • C_SignInit
    • C_SignRecoverInit
    • C_VerifyInit
    • C_VerifyRecoverInit
    • C_DeriveKey
    • C_DigestKey
    • C_WrapKey
    • C_UnwrapKey
    • C_FindObjects

The previous three functions deal with possible issues regarding keys that have been generated without Caml Crush. These keys can be dangerous because their values are known and they might be used to leak other keys. This patch works as follows:

  1. Paranoid mode: if CKA_SENSITIVE=TRUE and CKA_ALWAYS_SENSITIVE=FALSE, we
    do not trust the key and do not allow it to be used
  2. Relaxed mode for encryption keys (escrow usage): when used, this mode allows the
    usage of keys with CKA_SENSITIVE=TRUE and CKA_ALWAYS_SENSITIVE=FALSE ONLY if
    these are encryption/decryption keys and NON LOCAL keys
  3. Relaxed mode for all keys (not recommended): when set, this mode is the inverse of paranoid.
  • sticky_attributes_patch, patchset 1, applies to:
    • C_CopyObject
    • C_SetAttributeValue

The sticky attributes patch ensure that problematic attributes transition cannot achieved.

  • wrapping_format_patch, patchset 1, applies to:
    • C_WrapKey
    • C_UnwrapKey

This function is used to replace the classic C_WrapKey/C_UnwrapKey operations to protect from injecting rogue keys. You must configure the AES key (see wrapping_format_key) that will be used for CMAC to ensure integrity when using this function.

  • secure_templates_patch, patchset 2, applies to:
    • C_SetAttributeValue
    • C_GenerateKey
    • C_GenerateKeyPair
    • C_CreateObject
    • C_CopyObject
    • C_UnwrapKey
    • C_DeriveKey

This is the function that is applied for the second patchset, it is disabled by default. Only a controlled and safe set of critical attributes are allowed and are bound to a key when it is instantiated in a token (at key generation, key unwrapping,as well as key creation). Tying a key to its usage enforces key separation in the token, at the expense of imposing that the critical attributes become read-only. Though lacking flexibility, the main advantage of this patch lies in its full compatibility with the PKCS#11 standard.

  • sanitize_creation_templates_patch, patchset 1/2, applies to:
    • C_CreateObject
    • C_CopyObject
    • C_GenerateKey
    • C_GenerateKeyPair
    • C_DeriveKey
    • C_UnwrapKey

This sets default attributes values to be applied when not defined by a creation template.