From 24e8801d71a0f11dd64768cf961bd68f826eb362 Mon Sep 17 00:00:00 2001 From: gibbz00 Date: Tue, 26 Dec 2023 22:27:26 +0100 Subject: [PATCH] Update partial encryption in README.md. --- README.md | 49 ++++++++++++------- crates/rops/src/rops_file/mac.rs | 17 +++++++ .../sops_references/age_encrypted_suffix.yaml | 9 +++- .../age_encrypted_suffix_plaintext.yaml | 5 ++ 4 files changed, 60 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index db15b04..1aee05f 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ ## Goals * Full `sops` encrypted file compatibility. Decrypt any `sops` file using `rops` and vice versa. -* Available as a high quality rust library. +* Available as a rust library. ## Non-Goals @@ -23,30 +23,43 @@ - [ ] INI - [ ] ENV - [ ] BINARY -- Choice by CLI: +- [ ] Specify: - [ ] By flag: `--file-format`. - [ ] Infer by extension. + - [X] In library. - [ ] Partial encryption - - [ ] Either escape encryption: - - [ ] With a suffix. - - [ ] Flag: `--unencrypted-suffix`. - - [ ] `.rops.yaml`: `partial_encryption.escape.suffix`. - - [ ] Or through regex. - - [ ] Flag: `--unencrypted-regex`. - - [ ] `.rops.yaml`: `partial_encryption.escape.regex`. - - [ ] Or limit encryption: - - [ ] With a suffix - - [ ] Flag: `--encrypted-suffix`. - - [ ] `.rops.yaml`: `partial_encryption.limit.suffix`. - - [ ] Or through regex. - - [ ] Flag: `--encrypted-regex`. - - [ ] `.rops.yaml`: `partial_encryption.limit.regex`. - - [ ] Message authentication code (MAC) for encrypted parts only. + - [ ] CLI flag: `--{un,}encrypted-{suffix,regex} `. + - [ ] `.rops.yaml`: `partial_encryption.{un,}encrypted.{ match: {regex,suffix}, pattern: "" }`. + - [X] In library. + - [ ] MAC encrypted values only. - [ ] Flag: `--mac-only-encrypted`. - [ ] `.rops.yaml`: `partial_encryption.mac_only_encrypted: true`. - - [ ] In library - [ ] File comment encryption +#### Partial Encryption + +All keys are encrypted by default, unless one of `encrypted_suffix`, `encrypted_regex`, `unencrypted_suffix`, `unencrypted_regex` exists as a metadata setting. + +| Variant | Encrypt by default | Matched value | +| --- | --- | --- | +| `encrypted_{suffix,regex}` | No | Is encrypted | +| `unncrypted_{suffix,regex}` | Yes | Escapes encryption | + +Note that any matched key "locks" the triggered encryption config for all descendant key-value pairs. I.e. if the metadata contains `encrypted_suffix: "_encrypted"`, then the values for `i` and `ii` become encrypted in the map below: + +```yaml +foo: bar +nested_encrypted: + a: + i: xxx + b: + ii: xxx +``` + +##### Compute MAC for encrypted values only + +Unauthenticated changes in a plaintext value will still cause subsequent decryption attempts to fail. This is because all values are hashed into a message authentication code (MAC) before any encryption takes place. MAC verification will also fail after any unauthenticated addition, removal or reordering of values, regardless if they have been encrypted or not. The `mac_only_encrypted` metadata boolean can be enabled to allow for such unauthenticated modifications of plaintext key-value pairs, so long as the key paths for encrypted values remain the same. + ### Integrations: - [X] `age` diff --git a/crates/rops/src/rops_file/mac.rs b/crates/rops/src/rops_file/mac.rs index 7b49ec9..57e704a 100644 --- a/crates/rops/src/rops_file/mac.rs +++ b/crates/rops/src/rops_file/mac.rs @@ -251,6 +251,8 @@ mod mock { mod tests { #[cfg(feature = "sha2")] mod sha2 { + use indexmap::indexmap; + use crate::*; #[test] @@ -258,6 +260,21 @@ mod tests { assert_eq!(Mac::mock(), Mac::::compute(false, &RopsMap::mock())) } + #[test] + fn protects_against_collection_reordering() { + assert_ne!(mac_from_collection(&[1, 2, 3]), mac_from_collection(&[3, 2, 1])); + + fn mac_from_collection(ints: &[i64]) -> Mac { + let collection = ints.iter().map(|int| RopsTree::Leaf(RopsValue::Integer(*int))).collect(); + + let map = RopsMap(indexmap! { + "collection".to_string() => RopsTree::Sequence(collection) + }); + + Mac::compute(false, &map) + } + } + #[cfg(feature = "aes-gcm")] mod aes_gcm { use super::*; diff --git a/crates/rops/tests/sops_references/age_encrypted_suffix.yaml b/crates/rops/tests/sops_references/age_encrypted_suffix.yaml index 5616590..57cc18b 100644 --- a/crates/rops/tests/sops_references/age_encrypted_suffix.yaml +++ b/crates/rops/tests/sops_references/age_encrypted_suffix.yaml @@ -3,6 +3,11 @@ a: a: ENC[AES256_GCM,data:0FsM,iv:h1XsxkX+o1NT+D+XcFAODO43wuR8zysdDydI54dAuXo=,tag:EJ9xXVt7sKJUf/FLsiKP6g==,type:str] b: ENC[AES256_GCM,data:skTW,iv:bfM7BYJ6+mOnDd7kewCVqU2WW4jVPIRzRMwp2ojVPXw=,tag:mec4u6KOmvQroYJv/hqmeQ==,type:str] 2_plain: aaa + 3_nested_encrypted: + a: + i: ENC[AES256_GCM,data:gvZ5,iv:ARytT7LCYwrDBqdZ/SK4HCtnEmJo3JsATZuy2qcS7mE=,tag:+ekGmUFf9kdQhybUgou+Pg==,type:str] + b: + ii: ENC[AES256_GCM,data:ReUV,iv:YRASuxlwcayBPxQehJGzMSoKhFP+qL4Db/QqUNy76q4=,tag:2DRbJrykx/Q3L/FnAofLZA==,type:str] b_encrypted: ENC[AES256_GCM,data:MGQA,iv:MAMbnmDae/S6I/4HaN5A1xM2pPoUNFbCgr0UrOEnqFc=,tag:dGZBtt5npy+ig2DBbPC6Ug==,type:str] sops: age: @@ -15,6 +20,6 @@ sops: d29zcGlGNW5NelZFdWxCUjh1QVhvc2MKGnIPvwO1J8JvUOfDJodOC8AcAXEs8FqO cO+3ma+lL6B+Is3LOUDVM9WR6zTuOe95fqUlrDiWQJZrCQ4+WD0E4w== -----END AGE ENCRYPTED FILE----- - lastmodified: "2023-12-26T17:41:36Z" - mac: ENC[AES256_GCM,data:Kb3loHQyUQnjwwJTt2twBZtVURqnKw6wrZRiUUD+UbUH56eHjx0YAxEQsFJaASu3ulEAgQO90g9hWky+2yRj7cR7LGy+/DBW6RQVUbVMydHJb9Fd+Mm61KhEXUKfo1J6XF//J3NnCyJv2DKwrz6XsfzsDplH7IvlcItD81FvnLk=,iv:KnFGMSI4jN+00dHv97/oh1WFmBw6N6ywHqQgkh45Krs=,tag:EqTyKl/dGqpMUbe+GC28/g==,type:str] + lastmodified: "2023-12-26T20:11:30Z" + mac: ENC[AES256_GCM,data:lq5WAFq4ZPA8bYsdk/jM6fCrdmZce6tqtNmAgrU0mX+UkyKLV9QIpUPAg1e7AImgbTQMs/shmVuRYeFmHc+qhZ/pzoxFfRjcyargSwPM9EcvPVpgwAPylhezAwzLR5wteBxEjCZNRjFBnv0/qFrTW6uVoVWf96l3sMtgn9fbOog=,iv:6JhEg0HATLynl8P+dv0+kDY3x2tDdvmp/ZN7L63IX3I=,tag:9p9nbAYFauIpit4N9PapNw==,type:str] encrypted_suffix: encrypted diff --git a/crates/rops/tests/sops_references/age_encrypted_suffix_plaintext.yaml b/crates/rops/tests/sops_references/age_encrypted_suffix_plaintext.yaml index 0cfda76..22b96cf 100644 --- a/crates/rops/tests/sops_references/age_encrypted_suffix_plaintext.yaml +++ b/crates/rops/tests/sops_references/age_encrypted_suffix_plaintext.yaml @@ -3,4 +3,9 @@ a: a: xxx b: xxx 2_plain: aaa + 3_nested_encrypted: + a: + i: xxx + b: + ii: xxx b_encrypted: xxx