-
Notifications
You must be signed in to change notification settings - Fork 196
CFE-2613: Include and package cf-keycrypt with core #2883
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
For future reference, these were the compiler warnings of the unmodified cf-keycrypt source: |
6371c59 to
6377b5b
Compare
jimis
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First review, I only made it through a small section so more to come.
Also remove the commented out sections like // unused variable etc.
Thanks for starting this @olehermanse !
cf-keycrypt/cf-keycrypt.c
Outdated
| #include <known_dirs.h> | ||
| #include <dbm_api.h> | ||
| /* | ||
| #ifdef LMDB |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment can probably be removed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed
| */ | ||
|
|
||
| #include <config.h> | ||
| #include <cf3.defs.h> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Always first inclusion must be
- either
platform.h, which itself includesconfig.h. - OR
cf3.defs.hwhich includes itselfplatform.hand a bunch of libpromises stuff.
I think the 2nd is what you want here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
cf-keycrypt/cf-keycrypt.c
Outdated
| */ | ||
|
|
||
| #define STDIN 0 | ||
| #define STDOUT 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused defines. Remove?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed
cf-keycrypt/cf-keycrypt.c
Outdated
| printf("Workdir is defined as %s\n", WORKDIR); | ||
| printf( | ||
| "\n" | ||
| "Usage: cf-keycrypt [-e public-key|-d private-key|-H hostname-or-ip] -o outfile -i infile [-h]\n" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've never used this tool, but the command line usage that I would expect based on other tools is:
cf-keycrypt -e filename > file.out
cf-keycrypt -o file.out -e filename
Those two are equivalent. Similar for -d. Key is found in WORKDIR unless an option like --key=/path/to/key is specified.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Apparently the command needs more options than my previous examples. It needs a key file OR a hostname/IP.
-eor--encrypt-dor--decrypt-kor--keyfile-Hor--host-oor--outputfilenameshould be a mandatory argument
It's important to agree to a sensible command line usage before merging so that we don't break things later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think @nickanderson is in favor of getting this merged fairly quickly, and in the long term adding it's functionality to cf-key and deprecating this tool before the next LTS.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can tell you one thing: after it gets merged, nobody will have time to actually do anything. And we have time-based releases, not feature-based, so the release will happen.
So what you merge is what you'll get, almost certainly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's clean up the cli usage. I can always argue and beg for backporting, thats easier than allocating time to merge functionality into cf-key when we already have something that works and is standalone.
|
|
||
| for(res = result; res != NULL && !found; res = res->ai_next) | ||
| { | ||
| inet_ntop(res->ai_family, get_in_addr((struct sockaddr *)res->ai_addr), ipaddress, sizeof(ipaddress)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove get_in_addr() and inet_ntop(). The modern way to convert a struct sockaddr to textual representation of address is getnameinfo(). Example:
{
/* Convert IP address to string, no DNS lookup performed. */
char txtaddr[CF_MAX_IP_LEN] = "";
getnameinfo(ap->ai_addr, ap->ai_addrlen,
txtaddr, sizeof(txtaddr),
NULL, 0, NI_NUMERICHOST);
Log(LOG_LEVEL_DEBUG, "Bound to address '%s' on '%s' = %d", txtaddr,
CLASSTEXT[VSYSTEMHARDCLASS], VSYSTEMHARDCLASS);
}There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now that I think about all this whole address lookup function get_host_pubkey(), it has many issues in the lookups, and we have Hostname2IPString() for that. So better use that and save some code and risk.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jimis This is not a straight forward replace. Consider the steps here:
getaddrinfo()returns a list of results for the given hostname- Loop through the list. If
127.0.0.1or::1is in that list and beforeAddress2Hostkeyfinds a hostname, returnppkeys/localhost.pub - If
Address2Hostkeysucceeded during 2., returnppkeys/root-<KEY>.pub - If not loop through list again. Return any
ppkeys/root-<IP-ADDRESS>.pubwhere that file exists.
This is what I understand. I'm sure it's flawed, but what is the desired behavior here?
| struct addrinfo *res; | ||
| bool found = false; | ||
|
|
||
| int error = getaddrinfo(host, NULL, NULL, &result); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see getaddrinfo() but no freeaddrinfo(); leaking memory?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added.
| /* | ||
| cf-keycrypt.c | ||
|
|
||
| Copyright (C) 2017 cfengineers.net |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should add our copyright notice as well. Maybe also convert the language to GPLv3. Pinging @kacf and @nickanderson.
We should definitely mention the primary author of this in the CONTRIBUTORS file, and probably ping him in this pull request, what's his handle?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A file from external contributors should have the license intact, unless we've made substantial changes. Apache can be relicensed under GPLv3, so this is no problem for the project as a whole
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kacf given that we are making significant changes to it, I suggest keeping the initial license intact, but prepend our license and copyright for 2017. Sounds right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, that's fine!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Prepended our license.
jimis
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still have to review the actual crypto code.
cf-keycrypt/cf-keycrypt.c
Outdated
| for(res = result; res != NULL; res = res->ai_next) | ||
| { | ||
| inet_ntop(res->ai_family, get_in_addr((struct sockaddr *)res->ai_addr), ipaddress, sizeof(ipaddress)); | ||
| snprintf(buffer, BUFSIZE * sizeof(char), "%s/ppkeys/root-%s.pub", WORKDIR, ipaddress); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This whole thing can most probably be thrown out. I think we can just use crypto.c:HavePublicKey().
cf-keycrypt/cf-keycrypt.c
Outdated
| { | ||
| FILE *fp = NULL; | ||
| RSA* PRIVKEY = NULL; | ||
| static char *passphrase = "Cfengine passphrase"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This exists all over our codebase. Move all separate definitions to cf3.defs.h. Consolidate this readseckey() function together with the existing code in crypto.c, export the one from there and import it here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would rather not touch crypto or defsin this PR/commit. I can do that as a separate step after, if you'd like.
cf-keycrypt/cf-keycrypt.c
Outdated
| { | ||
| FILE *fp; | ||
| RSA *key = NULL; | ||
| static char *passphrase = "Cfengine passphrase"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same as above for this phrase and for readpubkey function.
cf-keycrypt/cf-keycrypt.c
Outdated
| int main(int argc, char *argv[]) | ||
| { | ||
| OpenSSL_add_all_algorithms(); | ||
| ERR_load_crypto_strings(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CryptoInitialize()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
|
Plenty of code style issues: braces not on separate lines, implicit comparisons, unneeded casts, uneeded parentheses etc. |
cf-keycrypt/cf-keycrypt.c
Outdated
| return -1; | ||
| } | ||
| } | ||
| fwrite(tmpplain,1,strlen(tmpplain),outfile); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This must be checked for error return.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed, and a bunch of other fread and fwrite.
cf-keycrypt/cf-keycrypt.c
Outdated
|
|
||
| ks = RSA_size(key); | ||
| tmpciph = (unsigned char *)malloc(ks * sizeof(unsigned char)); | ||
| tmpplain = (unsigned char *)malloc(ks * sizeof(unsigned char)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove casts and sizeofs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also use xmalloc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or just use stack allocated VLAs:
char tmpciph[ks], tmpplain[ks];There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
cf-keycrypt/cf-keycrypt.c
Outdated
| if ((size = RSA_private_decrypt(ks, tmpciph, tmpplain, key, RSA_PKCS1_PADDING)) == -1) | ||
| { | ||
| fprintf(stderr, "%s\n", ERR_error_string(ERR_get_error(), NULL)); | ||
| return -1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
File descriptor and memory leaks in case of error return. Not sure if this is a blocker though, given the short lifetime of the program.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
cf-keycrypt/cf-keycrypt.c
Outdated
| usage(); | ||
| exit(1); | ||
| } | ||
| exit(0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replace all zero and ones with EXIT_SUCCESS and EXIT_FAILURE. I would also suggest to just use return retval just at the end, if it's easy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
|
@jonhenrik13 We are adding cf-keycrypt to core :) You should probably take a look at this PR, it's not done yet though. |
|
@jimis @nickanderson Commenting here since https://tracker.mender.io/browse/CFE-2613 has the annoying Jira+Zendesk bug. Syntax will be: Only note here is that the current implementation doesn't support piping output to stdout, so I will have to add that. |
|
Syntax looks good to me. @nickanderson agree?
Yes if `-o` is missing, output should be always on stdout, I believe it
makes sense. BUT I see some conflicts with our `Log()` function, that
always logs to stdout.
…On Mon, Sep 25, 2017 at 8:16 PM, Ole Herman Schumacher Elgesem < ***@***.***> wrote:
@jimis <https://github.com/jimis> @nickanderson
<https://github.com/nickanderson> Commenting here since
https://tracker.mender.io/browse/CFE-2613 has the annoying Jira+Zendesk
bug.
Syntax will be:
cf-keycrypt --encrypt --keyfile /path/to/key.pub --output FILENAME.crypt FILENAME
cf-keycrypt -e -k /path/to/key.pub FILENAME > FILENAME.cfcrypt
cf-keycrypt --encrypt --keyfile /path/to/key.pub FILENAME > FILENAME.cfcrypt
cf-keycrypt --encrypt --host hostname(from cf-key -s) FILENAME > FILENAME.cfcrypt
cf-keycrypt --decrypt --keyfile /path/to/priv -o FILENAME.crypt FILENAME
Only note here is that the current implementation doesn't support piping
output to stdout, so I will have to add that.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#2883 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/ADayAiFyaL4J5g6Z-8Td_ypB3l0-7jvvks5sl-3-gaJpZM4OPCeD>
.
|
This was copied from https://github.com/cfengineers-net/cf-keycrypt Moved into core by Ole Herman Schumacher Elgesem <ole.elgesem@northern.tech> Changelog: Title Signed-off-by: Ole Herman Schumacher Elgesem <ole.elgesem@northern.tech>
a07e63e to
df6ea7f
Compare
|
Rebased on latest master. |
|
@jimis I slightly disagree with the syntax. My proposal is: It is almost the same, but input file must follow
This structure makes the most sense to me. |
|
I have implemented the new syntax, as explained in the previous comment. The new help menu looks like this: |
fba7bc5 to
f975a28
Compare
Also did a bunch of clean up Squashed commits: cf-keycrypt: Changed to spaces for indentation cf-keycrypt: Added Makefile cf-keycrypt: Added sys.cf-keycrypt variable(component) cf-keycrypt: Added automake subfolder cf-keycrypt: Added binaries to .gitignore cf-keycrypt: Added Makefile to configure.ac cf-keycrypt: Coding style cf-keycrypt: Fixed unused and uninitialized variables Changelog: Title
Squashed commits: cf-keycrypt: Fixes to acceptance test cf-keycrypt: Added to testall cf-keycrypt: Added keys and expected output cf-keycrypt: add seperate encryption and decryption tests Fixup encryption and decryption tests Really fix the encryption and decryption tests Expect encryption does not produce the same output cf-keycrypt: Add Example usage Fix example All the example pllicy blocks must be within the being and end src tags. Convert to static example
f975a28 to
332d302
Compare
|
@jimis Please review, I believe this is ready to merge. Will need to add an enterprise and buildscripts repo for it to be packaged. Changed the syntax as we discussed. I also did a lot of refactoring and error checking. Did not do big changes to internals like:
@nickanderson Maybe look over the commit messages/changelog entries before merge. |
|
This looks really good. I had to look hard to find some nit picks.
Does it output to stdout? if |
|
@nickanderson No, I removed the stdout and stdin functionality, mainly because it doesn't play well with our Log functionality. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice, acceptance tests! But the tool needs its own directory, it can't go under 00_basics. Try 27_cf-keycrypt.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, @nickanderson, what were you thinking? ;)
I believe this exact syntax you are describing will still be valid if Additionally in that way, the future functionality of missing
To me the syntax you described in your comment on Sep 25 is more familiar as it resembles gpg and a bunch of other really old UNIX encoding/decoding tools, like |
|
@jimis Correct me if I'm wrong but In that case, this:
is wrong. My current implementation uses the POSIX style |
|
Dear @olehermanse To implement as I say, you only have to make sure to parse your arguments with POSIX So what I said is right: you will be able to use your beautiful sentence-like syntax. It's not POSIX correct though, and it's not the documented way to use it, and I will still use the beautiful syntax with filename at the end, that I'm so used to in all utilities. So everyone is happy. :-) |
|
@jimis Please refrain from talking smack about the almighty Sarcasm aside, I'll make the fix today :) |
|
Dear @olehermanse, It is my understanding that this issue has been
resolved. I will not send any further messages.
…On Fri, Sep 29, 2017 at 3:52 PM, Ole Herman Schumacher Elgesem < ***@***.***> wrote:
@jimis <https://github.com/jimis> Please refrain from talking smack about
the almighty tape archiver - tar. I can change the syntax to your POSIX
hostile abomination. This has been noted down in your personal file. My way
was enforcing POSIX compliance (in terms of option order), and clearly far
superior, even though it used the GNU function.
Sarcasm aside, I'll make the fix today :)
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#2883 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/ADayAuTDNlEnsH9bZBusXJnm0Bu05W3aks5snPYRgaJpZM4OPCeD>
.
|
332d302 to
6f508ae
Compare
Also did a lot of refactoring, error checking, memory cleaning, et.c. Squashed commits: cf-keycrypt: license cf-keycrypt: PR fixes #1 cf-keycrypt: Fixed copyright in cf-keycrypt.c cf-keycrypt: CryptoInitialize() cf-keycrypt: Implemented new syntax as discussed in PR cf-keycrypt: Acceptance test for new arguments cf-keycrypt: Print help message when no arguments are added cf-keycrypt: PR fixes #2 cf-keycrypt: PR fixes #3 cf-keycrypt: PR fixes #4 cf-keycrypt: PR fixes #5 cf-keycrypt: Changed syntax to jimis' suggestion cf-keycrypt: Moved and fixed acceptance tests Changelog: Title Signed-off-by: Ole Herman Schumacher Elgesem <ole.elgesem@northern.tech>
6f508ae to
509f825
Compare
|
@jimis The syntax is now as you wanted. Input filename can be put anywhere. |
|
Started build in jenkins: @jimis I believe this is ready now, do you agree? |
|
EDIT: ignore me
|
|
@jimis Green on all except one platform |
|
The code quality looks good, and the command line too (despite the bike-shedding ;-) ). I'll now try to review the cryptographic part. |
jimis
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The most important comment is the RSA encryption mode. I need feedback from other people, like @estenberg, before merging.
I omitted reviewing the decrypt() function, but I assume the same notes apply.
| const char *pubkey_path, const char *input_path, const char *output_path) | ||
| { | ||
| int ks = 0; | ||
| unsigned long len = 0, ciphsz = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
len should be size_t since it's used with fread
| memset(tmp_ciphertext, '\0', ks); | ||
| len = fread( | ||
| tmp_plaintext, 1, ks - RSA_PKCS1_PADDING_SIZE, input_file); | ||
| if (len <= 0 || ferror(input_file) != 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
better declare len here not above, as size_t. Then it's gonna be obvious that testing it for negative values is bogus. Pity that the compiler does not complain, I'm quite sure a static analyzer would catch it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I have a theory: if you feed cf-keycrypt a file with size RSA_size(pubkey) - RSA_PKCS1_PADDING_SIZE, then it will output an error "could not read file". This test most likely needs to be if (len == 0 && ferror()).
| error_return = true; | ||
| break; | ||
| } | ||
| unsigned long size = RSA_public_encrypt( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
size should be int, because that's what RSA_public_encrypt returns and because it's compared to negative numbers later on.
| break; | ||
| } | ||
| unsigned long size = RSA_public_encrypt( | ||
| strlen(tmp_plaintext), tmp_plaintext, tmp_ciphertext, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shouldn't this be len? What happens if the file is binary and contains NULLs and you call strlen(tmp_plaintext) ?
| memset(tmp_plaintext, '\0', ks); | ||
| memset(tmp_ciphertext, '\0', ks); | ||
| len = fread( | ||
| tmp_plaintext, 1, ks - RSA_PKCS1_PADDING_SIZE, input_file); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So it keeps reading from a file 245 byte chunks, and RSA-encrypts each and every one separately using PKCS #1 v1.5 padding, and appends to a file, and each block is fully independent from the other without any chaining whatsoever. This is definitely fishy but I am not sure if there are specific vulnerabilities. Maybe @estenberg can comment ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jimis you are right, this is not good because 1) it leaks information as identical input chunks result in identical encrypted data 2) encrypting large volumes with a public key can leak information about the private key and 3) it is very inefficient (compared to symmetric crypto). At the very least we need to set a hard limit on the size (e.g. you can only encrypt 245 bytes or less), so it would really only work for password strings and the like.
The "right way" to do this is of course to encrypt a random symmetric key with RSA and then use that key with a block or stream cipher in a secure mode (e.g. CBC, CTR, etc.)
| ipaddress, | ||
| sizeof(ipaddress)); | ||
| snprintf( | ||
| buffer, BUFSIZE * sizeof(char), "%s/ppkeys/root-%s.pub", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't really use this kind of line breaking elsewhere in our code, unless it is almost unavoidable. Not a blocker, just keep in mind for next time.
|
Two high-level comments:
|
|
@jimis So I don't suspect I'll have time to rewrite the encryption code in at least a month. What do you want to do? Take a stab at it yourself, or wait until november? |
|
Not sure how to handle this, but we can't knowingly put in weak encryption. Lets bring this one up in the next planning meeting. P.S. to do proper public key encryption seems to be a bit complicated; found this article: https://shanetully.com/2012/06/openssl-rsa-aes-and-c/ |
|
@jimis I also found this which is a little more up to date, but doesn't show AES part. http://hayageek.com/rsa-encryption-decryption-openssl-c/ |
|
Great @olehermanse ! I'm all for doing this right, since Before coding stuff though we should make sure we are doing it the right way, maybe this is material for a meeting, or discussion on the relevant ticket. |
|
@jimis I know, hence why I said it didn't cover the AES step. (RSA to encrypt AES key, AES to encrypt data) |
|
Closing since CFE-2613 is not scheduled. Feel free to re-open when work resumes. |
The codebase has evolved since the last attempt to merge cf-keycrypt in cfengine#2883.
The codebase has evolved since the last attempt to merge cf-keycrypt in cfengine#2883. (cherry picked from commit bd3e840)
WIP, do not merge yet.
TODO:
cf-keycrypt.cwhich is from the cf-keycrypt repository