Attention une mise à jour du service Gitlab va être effectuée le mardi 30 novembre entre 17h30 et 18h00. Cette mise à jour va générer une interruption du service dont nous ne maîtrisons pas complètement la durée mais qui ne devrait pas excéder quelques minutes. Cette mise à jour intermédiaire en version 14.0.12 nous permettra de rapidement pouvoir mettre à votre disposition une version plus récente.

Commit 4ce34082 authored by Baobab Tac's avatar Baobab Tac
Browse files

Initial commit

parents
*.a
*.o
*.elf
*.swp
.classpath
.project
.project/
.settings/
target/
.vscode
c/build
# Proof of concept for the implementation of dynamic Location Specific Part (LSP) encoding/decoding according to the CLEA protocol
The Cluster Exposure Verification (Cléa) protocol is intended to notify participants of a private event (e.g. wedding or private party) or present in a commercial or public place (e.g. bar, restaurant, sports centre or train) that has become a cluster because a certain number of people present at the same time have been COVID+ tested. The protocol is described in detail in the document [The Cluster Exposure Verification (Cléa) Protocol: Specifications of the Lightweight Version](https://hal.inria.fr/hal-03146022/)
The aim of this development is to demonstrate an implementation of part of this protocol at the level of the dynamic Qr code encoding/decoding specification described in part `3.4- Dynamic QR code generation within the device` of the document.
In particular, it is foreseen that the display of the QR code dynamically is carried out by a specialised device with a microcontroller (example: MICROCHIP microcontroller, PIC32MM0256GPM036-I/M2) with low computing capacities. Moreover, it is planned to encrypt certain data using algorithms (e.g. ECIES-KEM) that are not necessarily found in 'standard off-the-shelf libraries'. This raised at least two points of attention which are illustrated in this implementation.
This repertoire therefore includes
* a C implementation (see [README.md](c/README.md)) of the LSP encoding (base64 data)
* a Java implementation (see [README.md](java/README.md)) of the encoding/decoding of LSP
* Tests to demonstrate a cycle of encoding, in C or Java, and decoding, in Java, of a LSP (see [README.md](test/README.md)).
This work can serve as inspiration for a C implementation dedicated to specialised devices or to more generic devices (PC, tablet, telephone) in Java.
cmake_minimum_required(VERSION 3.13)
project(clea C)
include(ExternalProject)
set(CMAKE_C_FLAGS "-W -Werror -Wextra -Wall -Wunreachable-code -pedantic -fno-builtin -std=c99 -DAES256=1 -DWITH_STDLIB=1")
if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
set(ECC_DEBUG debug)
else("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Os -fdata-sections -ffunction-sections")
set(CMAKE_EXE_LINKER_FLAGS "-Wl,--gc-sections -Wl,--strip-all")
endif("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
ExternalProject_Add(libecc
PREFIX libecc
GIT_REPOSITORY https://github.com/ANSSI-FR/libecc.git
GIT_TAG d26279b1e5e5e07bc21eb3bbe9d5cdc8bac4048e
GIT_PROGRESS true
PATCH_COMMAND patch -N -p1 < ${CMAKE_CURRENT_SOURCE_DIR}/libecc.patch
BUILD_IN_SOURCE true
CONFIGURE_COMMAND ""
BUILD_COMMAND make CFLAGS=${CMAKE_C_FLAGS} ${ECC_DEBUG}
INSTALL_COMMAND ""
)
ExternalProject_Add(tiny_aes
PREFIX tiny_aes
GIT_REPOSITORY https://github.com/kokke/tiny-AES-c.git
GIT_TAG 12e7744b4919e9d55de75b7ab566326a1c8e7a67
GIT_PROGRESS true
BUILD_IN_SOURCE true
CONFIGURE_COMMAND ""
BUILD_COMMAND make AES256=1 CBC=0 CTR=0 aes.a
INSTALL_COMMAND mv aes.a libaes.a
)
set(LIBECC_PATH ${CMAKE_CURRENT_BINARY_DIR}/libecc/src/libecc/src)
set(LIBTINY_AES_PATH ${CMAKE_CURRENT_BINARY_DIR}/tiny_aes/src/tiny_aes)
include_directories(${LIBECC_PATH} ${LIBTINY_AES_PATH} qrcodegen)
link_directories(${LIBECC_PATH}/../build ${LIBTINY_AES_PATH})
# CLEA library
add_library(clea STATIC aes-gcm hmac-sha256 ecies clea)
target_link_libraries(clea ec arith sign aes external_deps)
add_dependencies(clea libecc tiny_aes)
# Tests
add_library(test_util STATIC test_util)
add_executable(test_hmac-sha256-128 test_hmac-sha256-128)
target_link_libraries(test_hmac-sha256-128 clea)
add_executable(test_aes-gcm test_aes-gcm)
target_link_libraries(test_aes-gcm clea test_util)
add_executable(test_ecies test_ecies)
target_link_libraries(test_ecies clea test_util)
add_executable(test_clea test_clea)
target_link_libraries(test_clea clea test_util)
add_executable(simple_clea simple_clea)
target_link_libraries(simple_clea clea test_util)
# Raspberry PI specific rules
if("${PLATFORM}" STREQUAL "RPI")
ExternalProject_Add(openvg
PREFIX openvg
GIT_REPOSITORY https://github.com/ajstarks/openvg.git
GIT_TAG c6885d824eb8df762e1a654585bef54d9bf46764
GIT_PROGRESS true
BUILD_IN_SOURCE true
CONFIGURE_COMMAND ""
BUILD_COMMAND make
INSTALL_COMMAND ""
)
set(OPENVG_PATH ${CMAKE_CURRENT_BINARY_DIR}/openvg/src/openvg/)
include_directories(/opt/vc/include ${OPENVG_PATH})
link_directories(${OPENVG_PATH})
add_executable(simple_clea_rpi simple_clea_rpi qrcodegen/qrcodegen)
target_link_libraries(simple_clea_rpi clea shapes)
add_dependencies(simple_clea_rpi openvg)
endif("${PLATFORM}" STREQUAL "RPI")
\ No newline at end of file
# Proof of concept in C for the implementation of Location Specific Part (LSP) encoding according to the CLEA protocol
## Objectives
The QR code of a location/event, dynamic, which must be scanned at the entrance contains an URL ("deep link") structured by a prefix (for example for France: https://tac.gouv.fr/), followed by the 'location Specific Part' coded in base64. This directory gives an example of encoding in C language of the 'location Specific Part' of the QR code according to the [protocol Cléa](https://hal.inria.fr/hal-03146022).
This Proof of Concept can be used as a basis for:
* Validate the implementation of the encryption algorithms
* Generate locationSpecific Part for specialised devices
## Dependancies
* https://github.com/kokke/tiny-AES-c
* tiny_aes, AES128/192/256 in C small and portable
* license [unlicense](http://unlicense.org/)
* https://github.com/ANSSI-FR/libecc
* libecc, crypto. lib. based on elliptic curve
* Dual licenses BSD and GPLv2
## Files description
* aes_gsm encoding, wp_supplicant sources adaptation(license BSD) to implement GCM (Galois/Counter Mode) and GMAC
* `aes-gcm.c/h`: function `aes_gcm_encode` implementation
* `aes256-gcm_test_vectors.rsp`: Vectors test of [NIST](https://csrc.nist.gov/projects/cryptographic-algorithm-validation-program)
* `aes_internal.h`
* `test_aes256-gcm.py`: launching tests from test vectors (executable, need Python3)
* Implementation, based on [RFC2104](https://tools.ietf.org/html/rfc2104) and [RFC4868](https://tools.ietf.org/html/rfc4868)
* `hmac-sha256.c/h`: functions `hmac_sha256` et `hmac_sha256_128` implementation
* `test_hmac-sha256-128.c`: executable tests
* Implementation of the ISO18033-2 based scheme with the characteristics specified by Cléa:: key encapsulation ECIES-KEM, curve SECP256R1 is used for the pair keys ECDH, KDF1 based on HMAC-SHA256 and DEM based on AES-256-CTR
* `ecies.c/h` `ecies_init` and `ecies_encode` implementation
* `test_ecies.py`: executable tests on radom data
* LSP Cléa Encoding
* `clea.c/h` functions `clea_init`, `clea_start_new_period` and `clea_renew_qrcode` implementation
* `test_clea.c` executable test that periodically launch the generation of a 'location Specific Part' (LSP)
* `build_clea.c` executable used to test consistency with C encoding and Java decoding (see `../test`)
* `test_util.c/h` functions `parse` (char [] -> byte[]) et `dump` (print byte[]) for tests
* Exploring Qr code display on Raspberry Pi (sample work)
* `rpi_clea.c` example of QR code display on Raspberry Pi
* `openvg/` OpenVG graphics library adapted for Raspberry Pi
* `qrcodegen/` library for graphic generation of QR code https://www.nayuki.io/page/qr-code-generator-library
* `libecc.patch` patch to apply to the extern lib `libecc`. The extern libraries `libecc` et `tiny_aes` are git cloned via par the cmake process
* `CMakeLists.txt` Project compilation
## Installation and use
Clone the project and install it
```shell
> mkdir build
> cd build
> cmake ..
> make
```
```shell
>./test_aes-gcm
Usage: ./test_aes-gcm key iv plain_text aad cipher_text tag
>./test_hmac-sha256-128
Test #1 passed
Test #2 passed
Test #3 passed
>./test_ecies
Usage: ./test_ecies random priv_key data
> test_clea
AE7ovhtS8OmTGOVkk/5kcp6G0r5FBDCbQQffyCRE4U5LSG2Muxk12q/iWhpnHVucPMIkAJ5UZVFnfGHjmt3B8xFVERSoCT0fiBtFE7GGZUSQVvvZJ3ujtdLTqVVsO2ONOsAuRqHNOqXdzlQlWzua2X2qOahDScgC1IHe00ftKR6aOrdCpn1ZA2XJeWmt6wIyJX+XKF/qUqL7/p0Bj9NvorGxWRmIST3c+OFHkbBLsw==
AE7ovhtS8OmTGOVkk/5kcp6yyiQaTivN8kC8MT9FaGlcMYXetqJm9hzUQZhlvV4DQFrPdXASsNuHPfrbQWHkwLtktrEj/y6DuTwQz774KyVtknUE6oMpBp8inzQaHx4mrimPqQa1vEkI7BiRKKbtcVYT6H7LYcHZq5sOKCIE4Pmm6mJcGXmU6CJWMhVZpAORdEQswqnR9k1gNibRhhQhVmzs+WAGVOGJeANUyj+rzA==
> ./build_clea
Usage: build_clea staff CRIexp venueType venueCategory1 venueCategory2 countryCode periodDuration locationPhone locationPin PK_SA PK_MCTA
```
/*
* Adapted from wpa_supplicant code:
*
* Galois/Counter Mode (GCM) and GMAC with AES
*
* Copyright (c) 2012, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include <stddef.h>
#include "aes_internal.h"
#include "aes-gcm.h"
#include "aes.h"
static struct AES_ctx ctx;
static inline void memset(uint8_t *s, uint8_t v, uint8_t n)
{
uint8_t i;
for(i = 0; i < n; i++)
{
s[i] = v;
}
}
static inline void memcpy(uint8_t *d, const uint8_t *s, uint8_t n)
{
uint8_t i;
for(i = 0; i < n; i++)
{
d[i] = s[i];
}
}
static void inc32(uint8_t *block)
{
uint32_t val;
val = AES_GET_BE32(block + AES_BLOCK_SIZE - 4);
val++;
AES_PUT_BE32(block + AES_BLOCK_SIZE - 4, val);
}
static void xor_block(uint8_t *dst, const uint8_t *src)
{
uint8_t i;
for(i = 0; i < 16; i++)
{
dst[i] ^= src[i];
}
}
static void shift_right_block(uint8_t *v)
{
uint32_t val;
val = AES_GET_BE32(v + 12);
val >>= 1;
if (v[11] & 0x01)
val |= 0x80000000;
AES_PUT_BE32(v + 12, val);
val = AES_GET_BE32(v + 8);
val >>= 1;
if (v[7] & 0x01)
val |= 0x80000000;
AES_PUT_BE32(v + 8, val);
val = AES_GET_BE32(v + 4);
val >>= 1;
if (v[3] & 0x01)
val |= 0x80000000;
AES_PUT_BE32(v + 4, val);
val = AES_GET_BE32(v);
val >>= 1;
AES_PUT_BE32(v, val);
}
/* Multiplication in GF(2^128) */
static void gf_mult(const uint8_t *x, const uint8_t *y, uint8_t *z)
{
uint8_t v[16];
int32_t i, j;
memset(z, 0, 16); /* Z_0 = 0^128 */
memcpy(v, y, 16); /* V_0 = Y */
for(i = 0; i < 16; i++)
{
for(j = 0; j < 8; j++)
{
if(x[i] & 1 << (7 - j))
{
/* Z_(i + 1) = Z_i XOR V_i */
xor_block(z, v);
}
else
{
/* Z_(i + 1) = Z_i */
}
if(v[15] & 0x01)
{
/* V_(i + 1) = (V_i >> 1) XOR R */
shift_right_block(v);
/* R = 11100001 || 0^120 */
v[0] ^= 0xe1;
}
else
{
/* V_(i + 1) = V_i >> 1 */
shift_right_block(v);
}
}
}
}
inline static void ghash_start(uint8_t *y)
{
/* Y_0 = 0^128 */
memset(y, 0, 16);
}
static void ghash(const uint8_t *h, const uint8_t *x, uint8_t xlen, uint8_t *y)
{
uint8_t m, i;
const uint8_t *xpos = x;
uint8_t tmp[16];
m = xlen / 16;
for(i = 0; i < m; i++)
{
/* Y_i = (Y^(i-1) XOR X_i) dot H */
xor_block(y, xpos);
xpos += 16;
/* dot operation:
* multiplication operation for binary Galois (finite) field of
* 2^128 elements */
gf_mult(y, h, tmp);
memcpy(y, tmp, 16);
}
if(x + xlen > xpos)
{
/* Add zero padded last block */
uint8_t last = x + xlen - xpos;
memcpy(tmp, xpos, last);
memset(tmp + last, 0, sizeof(tmp) - last);
/* Y_i = (Y^(i-1) XOR X_i) dot H */
xor_block(y, tmp);
/* dot operation:
* multiplication operation for binary Galois (finite) field of
* 2^128 elements */
gf_mult(y, h, tmp);
memcpy(y, tmp, 16);
}
/* Return Y_m */
}
static void aes_gctr(const uint8_t *icb, const uint8_t *x, uint8_t xlen, uint8_t *y)
{
uint8_t i, n, last;
uint8_t cb[AES_BLOCK_SIZE], tmp[AES_BLOCK_SIZE];
const uint8_t *xpos = x;
uint8_t *ypos = y;
if(xlen == 0)
{
return;
}
n = xlen / 16;
memcpy(cb, icb, AES_BLOCK_SIZE);
/* Full blocks */
for (i = 0; i < n; i++)
{
memcpy(ypos, cb, AES_BLOCK_SIZE);
AES_ECB_encrypt(&ctx, ypos);
xor_block(ypos, xpos);
xpos += AES_BLOCK_SIZE;
ypos += AES_BLOCK_SIZE;
inc32(cb);
}
last = x + xlen - xpos;
if (last)
{
/* Last, partial block */
memcpy(tmp, cb, AES_BLOCK_SIZE);
AES_ECB_encrypt(&ctx, tmp);
for (i = 0; i < last; i++)
{
*ypos++ = *xpos++ ^ tmp[i];
}
}
}
static int32_t aes_gcm_init_hash_subkey(const uint8_t *key, uint8_t *H)
{
AES_init_ctx(&ctx, key);
/* Generate hash subkey H = AES_K(0^128) */
memset(H, 0, AES_BLOCK_SIZE);
AES_ECB_encrypt(&ctx, H);
return 0;
}
static void aes_gcm_prepare_j0(const uint8_t *iv, uint8_t iv_len, const uint8_t *H, uint8_t *J0)
{
uint8_t len_buf[16];
if(iv_len == 12)
{
/* Prepare block J_0 = IV || 0^31 || 1 [len(IV) = 96] */
memcpy(J0, iv, iv_len);
memset(J0 + iv_len, 0, AES_BLOCK_SIZE - iv_len);
J0[AES_BLOCK_SIZE - 1] = 0x01;
}
else
{
/*
* s = 128 * ceil(len(IV)/128) - len(IV)
* J_0 = GHASH_H(IV || 0^(s+64) || [len(IV)]_64)
*/
ghash_start(J0);
ghash(H, iv, iv_len, J0);
AES_PUT_BE64(len_buf, 0);
AES_PUT_BE64(len_buf + 8, iv_len * 8);
ghash(H, len_buf, sizeof(len_buf), J0);
}
}
static void aes_gcm_gctr(const uint8_t *J0, const uint8_t *in, uint8_t len, uint8_t *out)
{
uint8_t J0inc[AES_BLOCK_SIZE];
if(len == 0)
{
return;
}
memcpy(J0inc, J0, AES_BLOCK_SIZE);
inc32(J0inc);
aes_gctr(J0inc, in, len, out);
}
static void aes_gcm_ghash(const uint8_t *H, const uint8_t *aad, uint8_t aad_len, const uint8_t *crypt, uint8_t crypt_len, uint8_t *S)
{
uint8_t len_buf[16];
/*
* u = 128 * ceil[len(C)/128] - len(C)
* v = 128 * ceil[len(A)/128] - len(A)
* S = GHASH_H(A || 0^v || C || 0^u || [len(A)]64 || [len(C)]64)
* (i.e., zero padded to block size A || C and lengths of each in bits)
*/
ghash_start(S);
ghash(H, aad, aad_len, S);
ghash(H, crypt, crypt_len, S);
AES_PUT_BE64(len_buf, aad_len * 8);
AES_PUT_BE64(len_buf + 8, crypt_len * 8);
ghash(H, len_buf, sizeof(len_buf), S);
//aes_hexdump_key(MSG_EXCESSIVE, "S = GHASH_H(...)", S, 16);
}
/**
* aes_gcm_encode - GCM-AE_K(IV, P, A)
*/
int32_t aes_gcm_encode(const uint8_t *key, const uint8_t *iv, uint8_t iv_len, const uint8_t *plain, uint8_t plain_len, const uint8_t *aad, uint8_t aad_len, uint8_t *crypt, uint8_t *tag)
{
uint8_t H[AES_BLOCK_SIZE];
uint8_t J0[AES_BLOCK_SIZE];
uint8_t S[16];
if(aes_gcm_init_hash_subkey(key, H))
{
return -1;
}
aes_gcm_prepare_j0(iv, iv_len, H, J0);
/* C = GCTR_K(inc_32(J_0), P) */
aes_gcm_gctr(J0, plain, plain_len, crypt);
aes_gcm_ghash(H, aad, aad_len, crypt, plain_len, S);
/* T = MSB_t(GCTR_K(J_0, S)) */
aes_gctr(J0, S, sizeof(S), tag);
/* Return (C, T) */
return 0;
}
\ No newline at end of file
#ifndef AES_GCM__
#define AES_GCM__
#include <stdint.h>
#define AES_BLOCK_SIZE 16
int32_t aes_gcm_encode(const uint8_t *key, const uint8_t *iv, uint8_t iv_len, const uint8_t *plain, uint8_t plain_len, const uint8_t *aad, uint8_t aad_len, uint8_t *crypt, uint8_t *tag);
#endif
\ No newline at end of file
This diff is collapsed.
/*
* Adapted from wpa_supplicant code:
*
* Galois/Counter Mode (GCM) and GMAC with AES
*
* Copyright (c) 2012, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef AES_INTERNAL_H__
#define AES_INTERNAL_H__
#include <stdint.h>
static inline uint16_t AES_GET_BE16(const uint8_t *a)
{
return (a[0] << 8) | a[1];
}
static inline void AES_PUT_BE16(uint8_t *a, uint16_t val)
{
a[0] = val >> 8;
a[1] = val & 0xff;
}
static inline uint16_t AES_GET_LE16(const uint8_t *a)
{
return (a[1] << 8) | a[0];
}
static inline void AES_PUT_LE16(uint8_t *a, uint16_t val)
{
a[1] = val >> 8;
a[0] = val & 0xff;
}
static inline uint32_t AES_GET_BE24(const uint8_t *a)
{
return (a[0] << 16) | (a[1] << 8) | a[2];
}
static inline void AES_PUT_BE24(uint8_t *a, uint32_t val)
{
a[0] = (val >> 16) & 0xff;
a[1] = (val >> 8) & 0xff;
a[2] = val & 0xff;
}
static inline uint32_t AES_GET_BE32(const uint8_t *a)
{
return (a[0] << 24) | (a[1] << 16) | (a[2] << 8) | a[3];
}
static inline void AES_PUT_BE32(uint8_t *a, uint32_t val)
{
a[0] = (val >> 24) & 0xff;
a[1] = (val >> 16) & 0xff;
a[2] = (val >> 8) & 0xff;
a[3] = val & 0xff;
}
static inline uint32_t AES_GET_LE32(const uint8_t *a)
{
return (a[3] << 24) | (a[2] << 16) | (a[1] << 8) | a[0];
}
static inline void AES_PUT_LE32(uint8_t *a, uint32_t val)
{
a[3] = (val >> 24) & 0xff;
a[2] = (val >> 16) & 0xff;
a[1] = (val >> 8) & 0xff;
a[0] = val & 0xff;
}
static inline uint64_t AES_GET_BE64(const uint8_t *a)
{
return (((uint64_t) a[0]) << 56) | (((uint64_t) a[1]) << 48) |
(((uint64_t) a[2]) << 40) | (((uint64_t) a[3]) << 32) |
(((uint64_t) a[4]) << 24) | (((uint64_t) a[5]) << 16) |
(((uint64_t) a[6]) << 8) | ((uint64_t) a[7]);
}
static inline void AES_PUT_BE64(uint8_t *a, uint64_t val)
{
a[0] = val >> 56;
a[1] = val >> 48;
a[2] = val >> 40;
a[3] = val >> 32;
a[4] = val >> 24;
a[5] = val >> 16;
a[6] = val >> 8;
a[7] = val & 0xff;
}
static inline uint64_t AES_GET_LE64(const uint8_t *a)
{
return (((uint64_t) a[7]) << 56) | (((uint64_t) a[6]) << 48) |
(((uint64_t) a[5]) << 40) | (((uint64_t) a[4]) << 32) |
(((uint64_t) a[3]) << 24) | (((uint64_t) a[2]) << 16) |
(((uint64_t) a[1]) << 8) | ((uint64_t) a[0]);
}