Skip to content

Commit 0cd934b

Browse files
author
hg-anssi
committed
effacement sécurisé
1 parent 9daa41e commit 0cd934b

File tree

5 files changed

+212
-0
lines changed

5 files changed

+212
-0
lines changed

src/en/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
- [Naming](naming.md)
1414
<!-- - [Macros](macros.md) -->
1515
- [Integer operations](integer.md)
16+
- [Secure erasure](erasure.md)
1617
- [Error handling](errors.md)
1718
<!-- - [Type system](typesystem.md) -->
1819
- [Central traits](central_traits.md)

src/en/erasure.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Secure erasure
2+
3+
Zeroing memory is useful for sensitive variables, especially if the
4+
Rust code is used through FFI.
5+
6+
> **Rule {{#check MEM-ZERO | Zero out memory of sensitive data after use}}**
7+
>
8+
> Variables containing sensitive data must be zeroed out after use, using
9+
> functions that will not be removed by the compiler optimizations, like
10+
> `std::ptr::write_volatile` or the `zeroize` crate.
11+
12+
The following code shows how to define an integer type that will be set to
13+
0 when freed, using the `Drop` trait:
14+
15+
```rust
16+
/// Example: u32 newtype, set to 0 when freed
17+
pub struct ZU32(pub u32);
18+
19+
impl Drop for ZU32 {
20+
fn drop(&mut self) {
21+
println!("zeroing memory");
22+
unsafe{ ::std::ptr::write_volatile(&mut self.0, 0) };
23+
}
24+
}
25+
26+
# fn main() {
27+
{
28+
let i = ZU32(42);
29+
// ...
30+
} // i is freed here
31+
# }
32+
```

src/fr/SUMMARY.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@
99

1010
# Langage
1111

12+
- [Sémantique](semantics.md)
1213
- [Garanties du langage](guarantees.md)
1314
- [Nommage](naming.md)
1415
<!-- - [Macros](macros.md) -->
1516
- [Gestion des entiers](integer.md)
17+
- [Effacement sécurisé](erasure.md)
1618
- [Gestion des erreurs](errors.md)
1719
<!-- - [Système de types](typesystem.md) -->
1820
- [Traits centraux](central_traits.md)

src/fr/erasure.md

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
# Effacement sécurisé
2+
3+
L'effacement sécurisé (mise à zéro) est nécessaire pour les variables sensibles,
4+
en particulier dans lorsque le code Rust est utilisé *via* des FFI.
5+
6+
> **Règle {{#check MEM-ZERO | Mise à zéro des données sensibles après utilisation}}**
7+
>
8+
> Les variables contenant des données sensibles doivent être mises à zéro après
9+
> utilisation.
10+
11+
<!-- justification ? -->
12+
13+
<div class=warning>
14+
15+
Les opérations d'effacement de la mémoire son compliquées à réaliser en général, et
16+
avec Rust en particulier. En effet, comme en C, la sécurité de la mémoire n'est pas
17+
un comportement *observable*, c'est à dire dont le compilateur se porte garant.
18+
19+
Du fait des nombreuses optimisations[^note] du compilateur,
20+
les recommendations suivantes ne garantissent donc pas l'effacement complet des secrets
21+
après leur utilisation, mais sont données en *best-effort*.
22+
23+
</div>
24+
25+
[^note]: ... et éventuellement de celles futures !
26+
27+
28+
29+
## Forcer l'effacement
30+
31+
Une première difficulté naît des optimisations du compilateur : en général, il considère inutiles des opérations
32+
d'écriture en mémoire sans lecture subséquente et peut les supprimer.
33+
34+
Par exemple, dans le code suivant, l'écriture en mémoire suivante n'est pas effectuée
35+
36+
```rust
37+
pub fn main() {
38+
let mut n: u64 = 0x4953534e41;
39+
println!("{}", n);
40+
n = 0; // optimized away!
41+
}
42+
```
43+
44+
Il existe dans la bibliothèque standard Rust des fonctions `unsafe` forçant l'écriture en mémoire malgré les optimisations
45+
du compilateur. Par exemple, la fonction `std::ptr::write_volatile` ne sera jamais supprimée
46+
par le compilateur. Par exemple, la fonction suivante appliquera toujours l'effacement de la mémoire d'un entier.
47+
48+
```rust
49+
fn erase(mut n: u8) {
50+
println!("zeroing memory");
51+
unsafe { ::std::ptr::write_volatile(&mut n, 0) };
52+
}
53+
```
54+
55+
Cependant, cette fonction étant `unsafe`, on lui préférera si possible la crate `zeroize`.
56+
57+
> **Règle {{#check MEM-ZERO-EFFECTIVE | Effectivité de l'effacement de la mémoire}}**
58+
>
59+
> Les développements concernant la mise à zéro de variables sensibles
60+
> s'assureront de l'exécution effective de l'opération.
61+
62+
<!-- ## Éviter les duplications de secrets
63+
64+
Plus le nombre de secrets est grand plus il est difficile de s'assurer de leur non divulgation.
65+
Et même en supposant que leur effacement est automatisé (en utilisant `Drop`, ce qui,
66+
on le verra dans la suite, ne garantie par un effacement systématique), la multiplication
67+
des emplacements augmente le risque de divulgation -->
68+
69+
<!-- je n'en suis pas si sûr finalement -->
70+
71+
72+
## Déplacements de valeurs sensibles
73+
74+
La [sémantique par déplacement](semantics.md#déplacement-des-valeurs) de Rust induit une contrainte
75+
supplémentaire dans le contrôle des variables sensibles. Le déplacement est une simple copie
76+
bit à bit d'un emplacement à un autre, sans action paramétrable par l'utilisateur (excluant par exemple
77+
d'éventuel recours à `drop`).
78+
79+
De plus, le déplacement étant un détail d'implémentation du compilateur il est difficile de
80+
savoir à quel moment il interviendra.
81+
82+
> **Règle {{#check MEM-ZERO-NOMOVE | Absence de déplacements de valeurs sensibles}}**
83+
>
84+
> Les valeurs sensibles ne devront pas être déplacées après leur création.
85+
86+
Il s'en suit la recommendation suivante découlant directement de la précédente
87+
88+
> **Règle {{#check MEM-ZERO-BYREF | Absence de transfert de propriété d'une valeur sensible}}**
89+
>
90+
> La propriété d'un secret n'est pas transferable. Une fois le secret créé (même vide), les seules façons
91+
> permettant d'accéder à un secret sont
92+
>
93+
> * la copie (trait `Copy`) ou le clonage (trait `Clone`) uniquement si le type du secret implémente `Drop` dans laquelle le secret est effacé,
94+
> * la référence (`&T` ou `&mut T`)
95+
96+
<div class="warning">
97+
98+
Il est à noter que ces précautions ne garantissent pas le bonne effacement d'un secret : en effet,
99+
il est possible dans certaines situations que le compilateur ajoute des optimisations cassant les invariants
100+
mis en place pour empêcher le déplacement d'un secret.
101+
102+
Par exemple, il peut choisir de transformer un passage par référence en passage par valeur, ce
103+
qui met à mal la précaution [MEM-ZERO-BYREF](#MEM-ZERO-BYREF).
104+
105+
</div>
106+
107+
<!-- ### Fixer les valeurs sensibles en mémoire
108+
109+
Afin de se prémunir d'un déplacement involontaire, on utilisera le système de types pour vérifier à la
110+
compilation qu'une valeur est *fixée* en mémoire.
111+
112+
> **Règle {{#check MEM-ZERO-NOMOVE-TYPED | Épinglage des valeurs sensibles}}**
113+
>
114+
> Les fonctions manipulant les secrets devront, dans la mesure du possible, prendre en paramètre une version *épinglée*
115+
> du secret, s'assurant ainsi que le secret ne pourra être déplacé en mémoire.
116+
>
117+
> Par exemple, si `T` est le type d'un secret, on privilégiera la fonction
118+
>
119+
> ```rust
120+
> fn plop()
121+
> ``` -->
122+
123+
> **Règle {{#check MEM-ZERO-WRAPPED-INIT | Création de valeurs sensible dans un environnement sécurisé}}**
124+
>
125+
> Les valeurs sensibles seront initialisées dans un environnement s'assurant que ces valeurs seront bien
126+
> effacées après leur usage.
127+
128+
L'initialisation suivante est **mauvaise** car la valeur du secret est d'abord créée dans un environnement où l'effacement n'est pas assuré
129+
130+
```rust
131+
struct Secret {...}
132+
133+
impl Secret {
134+
/// This initialisation function is no secure since the content
135+
/// of the secret is first generated outside the secure wrapper [`Secret`]
136+
fn new(content : [u8; 16])
137+
}
138+
139+
impl Drop for Secret {
140+
fn drop(&self) {
141+
// securely erase the content of a [`Secret`]
142+
...
143+
}
144+
}
145+
```
146+
<!--
147+
## Exemple d'implementation d'un type de secret
148+
149+
On pourra s'inspirer de la librairie [secrust](https://github.com/hg-anssi/secrust) pour
150+
gérer ses secrets en suivant les recommendations de ce guide. -->

src/fr/semantics.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Sémantique
2+
3+
Les choix sémantiques de Rust, motivés en partie pour des besoins de sûreté,
4+
doivent être bien compris pour savoir
5+
6+
* s'appuyer dessus pour sécuriser son code
7+
* ne pas introduire d'effet indésirables
8+
9+
## Déplacement des valeurs
10+
11+
Par défaut, les valeurs de Rust sont considérées comme *déplaçable* à loisir en mémoire.
12+
On donne dans la suite quelques exemples de déplacements en mémoire.
13+
14+
* Le passage *par valeur* lors d'un appel de fonction peut déplacer en mémoire l'objet.
15+
Par exemple, la fonction `Box::new<T>(t : T) -> Box<T>` *déplace* la valeur `t`
16+
depuis la pile vers le tas.
17+
* Lors du redimensionnement d'un tableau dynamique de type `Vec<T>`, les éléments du
18+
tableau sont tous déplacés depuis l'ancien vers le nouveau tableau.
19+
* Lors du déréférencement d'une variable (par exemple `let y = *x;``x` est de type `Box<T>`),
20+
le contenu du tas est déplacé vers la pile.
21+
22+
Les déplacements sont de simples copies bit à bit depuis l'emplacement initial vers l'emplacement
23+
final, suivi d'une libération mémoire simple (sans `drop`) lors que l'emplacement initial se trouve
24+
sur le tas.
25+
26+
27+
Cette particularité complexifie notamment les [effacements sécurisés](erasure.md#fixer-les-valeurs-en-mémoire) de variables sensibles.

0 commit comments

Comments
 (0)