Authors:
(1) Simon Kafader, University of Bern, Bern, Switzerland ([email protected]);
(2) Mohammad Ghafari, University of Auckland, Auckland, New Zealand ([email protected]).
We developed FluentCrypto, a wrapper around the Node.js crypto API, with the aim of easing the secure adoption of crypto APIs for mainstream developers. FluentCrypto bridges the gap between cryptography experts and developers. It relaxes developers from crypto concerns such as the order of API calls, the choice of a secure algorithm, the right way to generate a key, etc, and when something goes wrong, it provides developers with guiding error messages to resolve the issue.
The work flow of FluentCrypto is shown in Figure 1. First, crypto experts should specify the secure configuration of crypto objects (i.e., security constraints) in a domain specific language, named CryRule, that we designed for this purpose. Then, developers write crypto code using the FluentCrypto that enables defining a crypto task and providing necessary configurations in a method-chaining style. FluentCrypto is built to be hard to misuse. It hides the complexities behind the scene i.e., developers only specify “what” they need without concerning about “how” to do it. Yet, it allows expert developers to provide their own crypto objects, if needed. Finally, i.e., at runtime, FluentCrypto initializes each crypto object with secure defaults that experts specified, and it validates each configuration provided by a developer against the latest security constraints to determine a correct use of cryptography. We employed a dynamic approach mainly due to the use of JavaScript, but it is worthwhile to investigate the possibility of adopting statical techniques to provide developers with JIT feedback especially in other settings.
FluentCrypto currently supports hashing, symmetric as well as asymmetric encryption. In the rest of this section, we only discuss symmetric encryption, which is known to be one of the most commonly used types of cryptography among developers [4]. FluentCrypto is publicly available on GitHub.[4].
A. CryRule
We developed a domain-specific language that crypto experts can use to specify secure configurations of cryptography objects independent from the actual cryptography code. We named this language “CryRule” and refer to its files (.cryrule) as “rule files”. We built CryRule using the chevrotain JavaScript library, an open-source parser building toolkit.[5] We also developed a parser that compiles CryRule files into plain JavaScript objects that we use to validate the crypto-related configurations at runtime and determine the secure use of crypto APIs.
There are many ways that developers can use a crypto API but only a few correspond to correct and secure usages. We therefore tailored CryRule to use white listing i.e., the rules indicate secure configurations whereas all deviations from such rules are assumed to be insecure. Each rule specifies constraints on the values of a parameter that a crypto API can hold. CryRule is not whitespace sensitive; neither line breaks nor indentations change the meaning.
A rule file in CryRule includes one or multiple sections. Each section describes the constraints on a certain cryptographic task, and starts off by specifying the name of individual classes, such as Cipher or Hash, that the crypto constraints apply to.
There are a few keywords in CryRule to define a rule. For instance, ALGORITHM defines the algorithm(s) that are allowed for a task. This keyword is followed either by a single algorithm name (e.g., Listing 2) or by the IN keyword and a list of algorithms inside the square brackets (e.g., Listing 3).
LENGTH defines a constraint on an object that has a length, e.g., a symmetric key or an IV. This keyword is followed by either a number or the keyword IN and an array of numbers, which are the lengths that the object is allowed to accept. The number(s) can be followed by an IF statement that declares the condition under which the specified constraint (in this case, the length) is valid. For example, Listing 4 specifies the constraints on the IV. In particular, it states that the IV should be 16 bytes if the cipher algorithm is “aes-128-cbc”, “aes-192-cbc”, or “aes-256-cbc”; and it must be 12 bytes if the cipher uses the “aes-128-gcm”, “aes-192-gcm”, or “aes-256-gcm” algorithm.
It is possible to specify constraints for a symmetric key using the SYMMETRICKEY keyword. It consists of multiple LENGTH constraints on the length of the key w.r.t. the algorithm used in the key generation process; a SALTLENGTH >= constraint to set the minimum length for the salt; and finally, an ITERATIONS >= constraint to set the minimum times that the key generation algorithm must repeat to generate a symmetric key.[6] The CryRule snippet presented in Listing 5 shows constraints on a symmetric key. The length of the key is bound to 16 bytes if the algorithm for the cipher task is either “aes-128-cbc” or “aes-128-gcm”; it is 24 if the cipher uses either “aes-192-cbc” or “aes-192-gcm” algorithm; and it must be 32 in case of “aes-256-cbc” or “aes-256-gcm” algorithm. The minimum number of iterations is set to be 10 000, and the salt is restricted to a length of at least 20 bytes.
It is worth noting that we derived the current set of rules based on our expertise and by consulting literature [18], however, experts can always update these constraints according to the latest standards and security advice.
B. The fluent interface
We developed, FluentCrypto, an API that enables easy and secure adoption of cryptography. It utilizes the fluent interface design principle to achieve method chaining, i.e., invoking multiple methods successively, so that it reads very similar to a natural-language text [23]. The order of method calls does not matter, and methods are named after the tasks they solve. FluentCrypto ensures a correct and secure adoption of cryptography in two ways. Firstly, developers only specify “what” they need without concerning about “how” to do it. Consequently, it clears implementation complexities such as calling the wrong methods on the API objects, calling methods in an incorrect order, or missing to call the methods entirely. For instance, FluentCrypto uses a secure source of randomness and generates a new IV for every encryption. Secondly, FluentCrypto relies on the constraints that crypto experts specify to determine a correct and secure configuration of the API such as the choice of algorithm and the length of an IV. Specifically, in the initialization of the encryption process, it parses the latest crypto constraints that are specified in the CryRule files and passes these rules to the related objects. When a crypto API function is called, all the associated rules are validated against the current configuration and an exception is thrown in case of a violation.
Listing 6 presents a very basic example of using FluentCrypto to perform encryption. In the first line, we access the FluentCrypto library. We tell the API that we want to perform an encryption task in the third line, and provide it with the plain input data in the fourth line. Finally, we call the run method to perform the actual encryption.
Even in the simplest encryption scenario, e.g., to encrypt data with a secret key, a developer should provide the encryption algorithm and the key. In order to facilitate the adoption of cryptography, FluentCrypto uses secure defaults for any input that is missing. A secure default is the first value that is associated with each constraint in the CryRule files. For instance, considering the code example in Listing 6 and the constraints in Listing 3Listing 4Listing 5, FluentCrypto would use “aes-128-cbc” algorithm; securely generate the right sizes of a key and an IV (i.e., 16 bytes); identify and set the right encoding i.e., utf-8. FluentCrypto uses pbkdf2 as its default key derivation function.
FluentCrypto provides a set of functions that allow developers to specify their own configurations and set up crypto objects manually, similarly to configuring the native API. A number of these functions are listed in Table I.[7] For instance, the withCipher(‘aes-192-cbc’) method instructs the API to use the AES-192 bit Cipher algorithm in Cipher Block Chaining (CBC) mode. A developer can set a key using the withSymmetricKey() method. If the given key features are not according to the chosen cipher algorithm, FluentCrypto will throw an error. However, it should be noted that when a developer inputs her own crypto object, such as a secret key, FluentCrypto cannot any longer determine whether the key is secure. Precisely, FluentCrypto runs the checks on the provided key i.e., the length constraint, but it cannot examine if the key was derived in a secure way.
Finally, in order to perform decryption, developers should simply invoke the Decryption function and provide all necessary inputs as shown in Listing 7. It is worth mentioning that FluentCrypto provides a set of getter methods, such as getKey and getIV, to access crypto-related objects that are used in an encryption task. These objects are required for a successful decryption operation.
[4] https://github.com/Smoenybfan/FluentCrypto
[5] https://github.com/SAP/chevrotain
[6] The order of constraints on a SYMMETRICKEY is important.