https://github.com/dmalikov/e
Express sensitive data inside text files in readable and compact way
https://github.com/dmalikov/e
encryption gpg gpgme haskell
Last synced: 6 months ago
JSON representation
Express sensitive data inside text files in readable and compact way
- Host: GitHub
- URL: https://github.com/dmalikov/e
- Owner: dmalikov
- Created: 2015-10-10T21:15:05.000Z (over 10 years ago)
- Default Branch: master
- Last Pushed: 2024-01-01T01:31:19.000Z (about 2 years ago)
- Last Synced: 2025-04-05T19:06:16.939Z (10 months ago)
- Topics: encryption, gpg, gpgme, haskell
- Language: Haskell
- Homepage: https://dmalikov.github.io/e/
- Size: 7.05 MB
- Stars: 4
- Watchers: 3
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.markdown
Awesome Lists containing this project
README
# e #
## What is it? ##
This toy project is an attempt to provide some kind of automation to hide sensitive data in text files, e.g. dotfiles.
It consists of:
- `e` - primitives library
- `e-aesgcm` - custom implementation of asymmetric encryption using AES-GCM on RSA keys
- `e-gpgme` - implementation of encryption over `gpgme` library
- `e-exe` - executable to encrypt/decrypt text files. Depends on `e`, `e-aesgcm` and `e-gpgme`
## What is it needed for? ##
### Goal ###
_Hide sensitive data inside text files in a readable and compact manner._
At the first glance, it's a pretty straight forward task, like, err, encrypt the file, and ... that's it.
However, it doesn't make much sense since:
- Is it really necessary to encrypt entire file just to hide a single password inside?
- How to reason about the rest of the file, since it's but an encrypted jibberish?
- How to have a readable diff when only plain text was changed?
- How to have a readable diff when only one of the sensitive values was changed?
- How to update all the usages of a given sensitive value in many files?
- etc.
All this can be summarized in some kind of requirements.
### Requirements ###
- Support various encryption/key-storing mechanisms (per encrypted value).
- Adding new portion of secrets to already encrypted text file should not modify previously encrypted values.
- Only sensitive data is hidden, the rest should be left intact.
- Ciphered values should not be part of the file (in order to make it readable and compact).
- Single ciphered value could be referenced many times.
- [TODO] Single ciphered value could be referenced in many files.
## Template syntax ##
`Tem` could contain plain text, plain values `PlainValue` that would be encrypted, and references to already encrypted values `ValRef`.
### 'PlainValue' syntax ###
`{{P||||}}`
E.g. `{{P|username|gpgme|keyId = foobar|bazqux}}` stands for sensitive content "bazqux" that need to be encrypted with "gpgme" algorithm with "foobar" keyId, and stored as "username".
### 'ValRef' syntax ###
`{{E|}}`
E.g. `{{E|username}}`.
- ``. Name of the variable stored in `Metadata` which could be referenced in `ValRef`.
- ``. `AlgName` provided by one of the algorithms `Algs` used for encryption/decryption.
- ``. Arguments that encryption algorithm `Algs` use during encryption/decryption.
- ``. Sensitive data to encrypt.
## How to install? ##
Run either `cabal new-build` or `stack build` in `e-exe` directory.
## How to use? ##
Let's try to hide sensitive data in `"password = qwerty123!"` string using `gpgme` `Alg` from `e-gpgme` package.
_Note: there is 8FCB631E gpg key used which is probably absent on your machine and should be substituted with another one._
### Using `e` library
```
$> cd e && cabal new-repl
ghci> :m + E E.Algorithm.Gpgme
ghci> :set -XTypeApplications
ghci> plain = pack "password = {{P|password|gpgme|keyId = 8FCB631E|qwerty123!}}"
ghci> Just tem = decode @Tem plain
ghci> tem
"password = " `Txt` (PlainValue (ValName {unValueName = "password"}) (AlgName {unAlgName = "gpgme"}) (Args {unArgs = fromList [(ArgName {unArgName = "keyId"},ArgValue {unArgValue = "8FCB631E"})]}) (PlainContent {unPlainContent = "qwerty123!"}) `Val` Nil)
ghci> Right (tem', meta) <- runExceptT $ encryptTem gpgme mempty tem
ghci> meta
Metadata {unMetadata = fromList [(ValName {unValueName = "password"},EncValue (AlgName {unAlgName = "gpgme"}) (Args {unArgs = fromList [(ArgName {unArgName = "keyId"},ArgValue {unArgValue = "8FCB631E"})]}) (EncContent {unEncContent = "hQIMA3lu0PjDFmd8AQ//Q/1zSmyYch297WLFFjkXCCD4cN0O3ydN+UmEBE+J+8pHFirH3d/GOb8o1d/W1zuvL+7VCjm1S2VbreXr66OSj6Ox+0sVmW1IKN3wJLfpSXKVxXq8zcWrHIU+HCI6CsCc/7bEgnlEs8Cgf5rUwHr+3kaXuvIpvNM6bbhAHWDsoQo+NnzmeER6geK+SwjHO+hFC3QuI+18uDDGxeayn4+QRfKFkDfbNlaTdh6WhL7ltMXXY+WYpz5fV9jrbH+ZBf1XBhrJNmonGC32Cq7RnFLBqkUmEaUIanCHHoCwh9nE8IONY1YOqv/KdS9hNie3NdArtSS/cGI6HGjda5J+c+mKAyxMdvnTXXDmTrArTeveifRB9+wqMUct1d9WfIsZ1lgly0/uJHhPWsiNNHQ+6BVW90qOIVZfxcjjw0aBrG1QNng9xAJjwEVs+UJ8CoIDJ8XPuhHF5VLz4Qg7odpQpueAOhgtFaGyKaxxMzC49huMNrOx1tpcDpdhef93QOz2JnY/jfQIeq+kiaZF1SxjMoKZMXD1Xd0Yd73d3feqCE3FOrWVHm7NrU16ADk4Xa7m/kYzygaBo4Q+nDa2UFo1ExyD0Uqt8ZqjjoHpk+v9GWjISvPgGjVaHaS961OWR9HC1cu6S2L8tjwjrwOaHlI+fDTpgmfInHK/DaTZHCxN0IuYHdPSRQFKUKafXUo9Q+McvgJIo9RdMb6JAbPoLdep5Rda+WZTeser+MhkN77mcJ+ChrZSaWz2p9oH5QQwy/MO+UDTxgjsNfIFXw=="}))]}
ghci> tem'
"password = " `Txt` (ValRef {unValRef = ValName {unValueName = "password"}} `Ref` Nil)
ghci> encode tem'
"password = {{E|password}}"
ghci> Right tem'' <- runExceptT $ decryptTem gpgme meta tem'
ghci> tem''
"password = " `Txt` ("qwerty123!" `Txt` Nil)
ghci> encode tem''
"password = qwerty123!"
```
### Using `e` executable
```
$> cat file.config
username = dmalikov
password = qwerty123!
$> vim file.config # wrap sensitive strings in a 'PlainValue's
$> cat file.config
username = {{P|username|gpgme|keyId = 8FCB631E|dmalikov}}
password = {{P|password|gpgme|keyId = 8FCB631E|qwerty123!}}
$> e enc file.config
$> cat file.config
username = {{E|username}}
password = {{E|password}}
$> cat .file.config.e
{
"username": {
"alg": "gpgme",
"value": "hQIMA3lu0PjDFmd8AQ//bGsUrrpskpfHCwB68IKCfjZQ7p3GOGZXzTjEGGCynsEqAv/5DnkVB9XGGDbatgEBFcbiIb9f6bIcyzXZKF9CLq6+cxXyExqKTHornfrGjTUP2j2hf0bKbVFZ6DtADpQwquBiohoeUDVwQ2pVOKMkWfNkyv/Re1nIP2LBnxdbItn3C6mYSMb7FtLHxRj6EI8SuA83hmCu4Cu8mpsk08OHOXIerm9kzm63SsS6XGyq1JiE//5nb6cSG4SUgeV0d+WHX3IAmzapvqCQHXFfNPqbJj8NKjhk435gYk8i08FwOkP9+Ur7jjbzzQYvPD+0eFgrdUVIlSX00h1dyV8X99n0K41qNEgWY5AA22l4vK77To79qcvgJjwOENbv1RTrK3uYftcsg+8yg8SOyVQ4JBaBx84/hot9voC8aEImY098KbLla73weSwcb5p5gwJvK33xeT4FvgD0IJHJhmkNO5U6xp/hiAqPUdDseXCO3QjIocINfiH2sxJxbbpQ01Mx7ernLz1vFCxCnt1CjIvaCtDpmaWWbx8KVnMz/QYlo8hpT0JGu7DgETujJDkWRWZx3opF61bn2PW+Nh552kmnizAJl3ImZAmqPp1f1VKVU9/BAXNNpQfXrE129JUT7Z5h11sqcQummultPg/BogPN8WLneMFqGzL9kfQtGWtySQWJDWnSQwEBc2V+E1WzBHcUvm5t4A3VHR/VT1U65/dstfeZgY6W0Xc/nkt0KL0i3yXOVkRtvncyDlceecNEHWAUenTEIOAPsfM=",
"args": {
"keyId": "8FCB631E"
}
},
"password": {
"alg": "gpgme",
"value": "hQIMA3lu0PjDFmd8AQ//Zc2ycATx6ZdMjG15yNSz8CBjvj3afodyPJuFSWoelagn5m0k4YgfphD1ylzzESKNd9e0UJs/DDjGcEdaeg0KCytMmxDrz1+B9J2fmeiKzXIWi/wPgHjxeaxtTpnc+TlewCvO2dyGYvOCMNM3RbXKISpnYfT0Zt8wc/4V3NGiX7uD0dUq9jnyyH5vvuOEyJzEQ/BhJPgCeGFBIQWn1i/udDqWtWPGVg7cFet4zlNLBfVgCmOxNzi0jO3hKguERQjhzzgMHlq4TYIvRyMj/92hw9fD3kbjefmOT97NSl7mTwztsEykKNShnsj4HQeNwuBbs84uprNBEk+Sv4tn2+WSdNDPhPJ2DSjnTRYm6gX+WqPeJwUAmaPBNlW05rJjWRXwF4gr2vydlEEwRgaz7ONB4c2bmcAggAaSJA5dodt04oZ5gd2+vSzUpw4GKNhhoGefX6PtPO6DHC243cnQw9ACWhc4N2KBqLZYlEfa0qd641Z5265SC1953Ug5lLD654rrAJbAkLsVdYrfEftCFL2D1QzP81aE7QAvDFkMCdn6BUNEVbaExz9a06H0ZbFsFgC4mrKUA/3u9Iziu5KxUJRbmW0Nsdaz+lw3JSWPBGslOf8weDuYOMcjWPTWDwmkV5LAGqnKU8dofVQ4F0/8Yvtz3Fitro5QJWJ0Pm+rMgeyCrnSRQHywUH7cnB1K7sD1Hgq/LyGq0lVvHnSM5JDnU+9H349h5m/ldBz8YUkZkcrPRwZK29acqqcTJwNRU8fkud0JjwU5vhsLw==",
"args": {
"keyId": "8FCB631E"
}
}
}
$> e dec file.config
$> cat file.config
username = dmalikov
password = qwerty123!
```
🎉
## How to add support for another encrypting mechanism? ##
Let's take a look at `encryptTem` and `decryptTem`:
```
encryptTem :: Algs -> Metadata -> Tem -> ExceptT EError IO (Tem, Metadata)
decryptTem :: Algs -> Metadata -> Tem -> ExceptT EError IO Tem
```
In order to encrypt/decrypt template with custom `Algs` you need to define one and just pass there.
Since `Algs` is a `Monoid`, you can use many of them: `aesgcm <> gpgme <> custom`.
`E.Algorithm.Dummy` provides an example of a simplest implementation of encrypting mechanism suitable for `Tem`.
```
dummy :: Algs
dummy = algorithm (AlgName "dummy") (Cipher dummyCipher) (Decipher dummyDecipher)
where
dummyCipher _ (PlainContent c) = pure $ EncContent c
dummyDecipher _ (EncContent c) = pure $ PlainContent c
```
`AlgName` and a couple of routines (`Cipher` and `Decipher`) is all that needed.
After that `encryptTem` could be used with it, like `encryptTem (dummy <> gpgme) mempty tem`.