178 lines
11 KiB
Markdown
178 lines
11 KiB
Markdown
# Implementation
|
|
|
|
This section describes the salient implementation details of the Zencode DSL, the smart-rule language for DECODE, tailored on its use-cases and based on the Zenroom controlled execution environment (VM). Implementation details refer only to Zencode and not to how Zenroom is implemented, since the latter is already covered in other documents.
|
|
|
|
The implementation section contains three parts explaining:
|
|
- the language model inherited by Behaviour Driven Development
|
|
- the data validation model based on Schema Validation
|
|
- the implementation of implicit certificates
|
|
|
|
## Behaviour Driven Development
|
|
|
|
In Behaviour Driven Development (BDD), the important role of software integration and unit tests is extended to serve both the purposes of designing the human-machine interaction flow (user journey in UX terms) and of laying down a common ground for interaction between designers and stakeholders. In this Agile software development methodology the software testing suite is based on natural language units that grant a common understanding for all participants and observers.
|
|
|
|
To implement BDD the first step is that of mapping a series of interconnected cascading sentences to actual source code; this implementation is usually done manually by programmers that have knowledge of the higher level application protocol interface (API) that grants communication between the backend and the frontend of a software application. The BDD implementation can then be seen as an alternative frontend whose purpose is that of lowering the distance between expression and execution by means of utterances expressed in human language.
|
|
|
|
Far from giving an exhaustive description of BDD implementations and characteristics, this brief chapter intends to summarise the features of this approach where they specifically apply to the development goals of Zencode (previously stated) and the solution provided.
|
|
|
|
Referring to the Cucumber implementation of BDD, arguably the most popular in use by the industry to day and factual standard, the grammar of utterances is very simple and definable as a "cascading" flow indeed, since the fixed sequence of lines can follow only one fixed order:
|
|
|
|
- Given
|
|
- and*
|
|
- When
|
|
- and*
|
|
- Then
|
|
|
|
This sequence is fixed and in simple terms consists of an extendable initialisation of states "Given (and)*" followed by an extendable transformation of states "When (and)*" and concluded by a non-extendable enunciation of states in their final form "Then".
|
|
|
|
The Zenroom implementation is kept simple at this stage and does not takes any "fuzzy" approach to the parsing, but simply defines fixed sequences of strings and variables that are expected to occur within them: the variables are what is ultimately possible to change by users and are marked by a repeating sequence of two adjacent single quotes ('').
|
|
|
|
The underlying parser acts upon a positive, unique and so far non-flexible match of the whole phrase minus the variables, then executes a function that takes as many arguments as the variables present in the lines across the utterance. As a result, every single non-repeating line of the utterance has a declared function that interacts with the underlying implementation of Zenroom, whose actions are defined in its LUA subset language.
|
|
|
|
Brief examples of this implementation follow:
|
|
|
|
```
|
|
Given("I introduce myself as ''", function(name) whoami = name end)
|
|
Given("I am known as ''", function(name) whoami = name end)
|
|
```
|
|
|
|
The above definition of two lines possibly occurring within the utterances in Zencode are demonstrating how a state "who am I" basically my own name can be set using two different phrases, leading to the execution of the same function which basically operates a simple assignment to the variable *whoami*. This simple demonstration is a hint to the fact that multiple patterns can be defined also in different ways, making the Zencode DSL implementation very easy to translate across different spoken languages as well contextualised within specific idiolects adopted by humans.
|
|
|
|
Furthermore, another example of implementation:
|
|
|
|
```
|
|
Given("that '' declares to be ''",function(who, decl)
|
|
-- declaration
|
|
if not declared then declared = decl
|
|
else declared = declared .." and ".. decl end
|
|
whois = who
|
|
end)
|
|
Given("declares also to be ''", function(decl)
|
|
ZEN.assert(who ~= "", "The subject making the declaration is unknown")
|
|
-- declaration
|
|
if not declared then declared = decl
|
|
else declared = declared .." and ".. decl end
|
|
end)
|
|
```
|
|
|
|
Shows how is possible to accept multiple variables and process them through more complex transformations that also contemplate the concatenation of contents to previous states. States are in fact permanent within the scope of the execution of a single utterance and will be modified in the same deterministic order by which they are expressed across lines. What is also visible within this example implementation, which we intend to facilitate by customisation made by people who have a simple knowledge of Zenroom's API and LUA scripting, is that the 'ZEN.' namespace makes available a number of utility functions to easily check states (asserts) and propagate meaningful error messages that are then part of a backtrace output given to the calling application (host) on occurrence of an error.
|
|
|
|
The full implementation of Zencode available at the time of publishing this document is inside the source-code files 'zenroom/src/lua/zencode_*' and is relatively easy to maintain for the pilots analysed in our project, as well easy to extend to more use-cases. The current implementation addresses specific schemes that useful to the pilots in DECODE, while contemplating future extension:
|
|
- Simple symmetric encryption of ciphertext by means of a PIN and KDF transformations (pilot: Amsterdam Register)
|
|
- Diffie-Hellman asymmetric key encryption (AES-GCM) (pilot: Making Sense IoT)
|
|
- Blind-sign credentials for unlinkable selective attribute revelations[^coconut] (pilot: DECIDIM and Gebied Online)
|
|
In addition there is also the implementation of an "implicit certificate" crypto scheme (Qu-Vanstone, ECQV) that is limited to first order curve transformations, which may apply to pilots requiring simple certification schemes[^ecqv]. All the implementations are illustrated in more detail in the following chapters.
|
|
|
|
|
|
[^coconut] This implementation refers to work on the Coconut credential system (Sonnino et. al, 2018) designed after specific needs in DECODE's pilots. It does not implement, however, the threshold issuance part, which is only required in the scenario of a fully open blockchain implementation, which is still work in progress.
|
|
|
|
|
|
[^ecqv] It is important to note that while the ECQV scheme was not examined by other partners in our project, it has been choosen for its stable role in the industry and for its augmented complexity within an approachable implementation, complexity which could better inform the Zencode implementation.
|
|
|
|
|
|
|
|
## Declarative Schema Validation
|
|
|
|
In order to make the processing of Zencode more robust, all data used as input and output for its computations is validated according to predefined schemas. This makes the Zencode DSL a declarative language in which data recognition is operated before processing.
|
|
|
|
The data schemas are added on a per-usecase basis: they refer to specific cryptographic implementations as they are added in Zencode. Careful evaluation regarding their addition is made to realise if old schemas can be extended to include new requirements.
|
|
|
|
Schemas are expressed in a simple format using Lua scripting syntax, for example:
|
|
|
|
```
|
|
-- zencode_keypair
|
|
keypair = S.record {
|
|
schema = S.Optional(S.string),
|
|
private = S.Optional(S.hex),
|
|
public = S.ecp
|
|
}
|
|
```
|
|
|
|
The schema above is the smallest and most commonly used one, composed by one required field and two optional ones, used to validate the input and output of public/private keypairs to be used in transformations.
|
|
|
|
The only required field in the schema is the 'public' key which is validated using the 'ECP' type ('S.' is an abbreviation for the 'SCHEMA.' namespace). The validation of 'S.ECP' is an actual cryptographic validation: Zenroom will check that the big integer number represented by the field corresponds to a valid point on the curve. In case the validation is not passed, the execution of the Zencode script will not take place and Zenroom will return a meaningful error message indicating the wrong field.
|
|
|
|
The other optional field is the 'private' key which can correspond to any sequence of values, therefore no cryptographic validation is possible for it; in this case then the validation used is one that refers to the encoding of the field: 'S.hex' is verifying that the value is encoded with a sequence of characters that express only hexadecimal numbers (that is, 0..9 numbers and case-insensitive letters from A to Z). Other encoding tests are also available, for instance 'S.base64' if that is the encoding used in the specific implementation.
|
|
|
|
Another more complex example follows:
|
|
|
|
```
|
|
-- packets encoded with AES GCM
|
|
AES-GCM = S.record {
|
|
checksum = S.hex,
|
|
iv = S.hex,
|
|
schema = S.Optional(S.string),
|
|
text = S.hex,
|
|
zenroom = S.Optional(S.string),
|
|
encoding = S.string,
|
|
curve = S.string,
|
|
pubkey = S.ecp
|
|
}
|
|
```
|
|
|
|
In this example no new validations are being used and in fact it just adds fields compared to the previous: it defines a portable packet of ciphertext data that is returned as output of AES-GCM asymmetric encryption as well is accepted as input to AES-GCM decryption. A similarity between these two examples is evident: the presence of the 'schema' field. This field is a sort of "introspective" indication matching the data structure to its schema specification. If this field is not present (as it is always optional) then no validation on the data structure will take place, meaning the Zencode implementation leaves the risk (and hopefully the validation task) to the host.
|
|
|
|
This chapter ends with the current implementation of schema validation data types that are currently implemented for symmetric and asymmetric encryption of ciphertexts as well for implicit certificates. The schema implementation for Zencode is maintained into the sourcecode within the source file 'src/lua/zencode_schemas.lua' and can be accessed by the function 'ZEN.validate(data,'schema','error')' which is a wrapper of 'ZEN.assert(validate(data,schemas['schema']),'error')'.
|
|
|
|
|
|
```
|
|
_G['schemas'] = {
|
|
|
|
-- packets encoded with AES GCM
|
|
AES-GCM = S.record {
|
|
checksum = S.hex,
|
|
iv = S.hex,
|
|
schema = S.Optional(S.string),
|
|
text = S.hex,
|
|
zenroom = S.Optional(S.string),
|
|
encoding = S.string,
|
|
curve = S.string,
|
|
pubkey = S.ecp
|
|
},
|
|
|
|
-- zencode_keypair
|
|
keypair = S.record {
|
|
schema = S.Optional(S.string),
|
|
private = S.Optional(S.hex),
|
|
public = S.ecp
|
|
},
|
|
|
|
-- zencode_ecqv
|
|
certificate = S.record {
|
|
schema = S.Optional(S.string),
|
|
private = S.Optional(S.big),
|
|
public = S.ecp,
|
|
hash = S.big,
|
|
from = S.string,
|
|
authkey = S.ecp
|
|
},
|
|
|
|
certificate_hash = S.Record {
|
|
schema = S.Optional(S.string),
|
|
public = S.ecp,
|
|
requester = S.string,
|
|
statement = S.string,
|
|
certifier = S.string
|
|
},takes
|
|
|
|
declaration = S.record {
|
|
schema = S.Optional(S.string),
|
|
from = S.string,
|
|
to = S.string,
|
|
statement = S.string,
|
|
public = S.ecp
|
|
},
|
|
|
|
declaration_keypair = S.record {
|
|
schema = S.Optional(S.string),
|
|
requester = S.string,
|
|
statement = S.string,
|
|
public = S.ecp,
|
|
private = S.hex
|
|
}
|
|
|
|
}
|
|
```
|
|
|
|
|