Skip to content

Commit 2c9b6aa

Browse files
committed
refname encryption
1 parent 365b5fc commit 2c9b6aa

File tree

3 files changed

+211
-62
lines changed

3 files changed

+211
-62
lines changed

config.mak

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ OBJECTS += incrypt-plugin.o
55
MAN1_TXT += git-incrypt.adoc
66

77
incrypt-plugin.so: incrypt-plugin.o GIT-LDFLAGS $(GITLIBS)
8-
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) -shared $(filter %.o,$^) $(LIBS) $(LIB_4_CRYPTO)
8+
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) -shared $(filter %.o,$^) \
9+
$(LIB_4_CRYPTO) $(LIBS)

git-incrypt

Lines changed: 41 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -27,20 +27,23 @@ import os
2727
import sys
2828
import re
2929
import hashlib
30-
import base64
3130
import argparse
3231
import enum
3332
import ctypes
33+
import math
3434
import pygit2
3535

3636
plugin = ctypes.cdll.LoadLibrary(os.path.join(os.path.dirname(__file__),
3737
'incrypt-plugin.so'))
38+
plugin.setkey.argtypes = [ctypes.c_void_p]
3839
plugin.encryptdata.argtypes = [
3940
ctypes.c_void_p, ctypes.c_size_t,
40-
ctypes.c_void_p, ctypes.POINTER(ctypes.c_size_t), ctypes.c_void_p]
41+
ctypes.c_void_p, ctypes.POINTER(ctypes.c_size_t)]
4142
plugin.decryptdata.argtypes = [
4243
ctypes.c_void_p, ctypes.c_size_t,
43-
ctypes.c_void_p, ctypes.POINTER(ctypes.c_size_t), ctypes.c_void_p]
44+
ctypes.c_void_p, ctypes.POINTER(ctypes.c_size_t)]
45+
plugin.encryptrefname.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
46+
plugin.decryptrefname.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
4447

4548
if not hasattr(pygit2.enums, 'FileMode'):
4649
class FileMode(enum.IntFlag):
@@ -74,6 +77,7 @@ decrypt the repository using
7477

7578
def encryptdata(data: bytes, key: bytes) -> (bytes, bytes):
7679
'encrypt raw data'
80+
plugin.setkey(key)
7781
output = ctypes.create_string_buffer(len(data) + 16)
7882
poutput = ctypes.c_void_p(ctypes.addressof(output))
7983
outlen = ctypes.c_size_t(0)
@@ -84,6 +88,7 @@ def encryptdata(data: bytes, key: bytes) -> (bytes, bytes):
8488

8589
def decryptdata(ciphertext: bytes, key: bytes) -> bytes:
8690
'decrypt raw data'
91+
plugin.setkey(key)
8792
output = ctypes.create_string_buffer(len(ciphertext) + 16)
8893
poutput = ctypes.c_void_p(ctypes.addressof(output))
8994
outlen = ctypes.c_size_t(0)
@@ -94,31 +99,43 @@ def decryptdata(ciphertext: bytes, key: bytes) -> bytes:
9499

95100
def encryptrefname(ref, key):
96101
'encrypt a refname'
97-
def splitref(s):
98-
'split the ref from the suffix'
99-
match = re.search(r'[~^]', s)
100-
if match:
101-
index = match.start()
102-
return s[:index], s[index:]
103-
return s, ''
104-
105-
sref = splitref(ref)
106-
rawname = sref[0].encode('utf-8')
107-
return base64.b64encode(encryptdata(
108-
hashlib.sha1(rawname).digest() + rawname, key),
109-
b'+#').decode('utf-8') + sref[1]
102+
plugin.setkey(key)
103+
output = ctypes.create_string_buffer(
104+
math.ceil((20 + len(ref) + 16) * 4 / 3) + 1)
105+
poutput = ctypes.c_void_p(ctypes.addressof(output))
106+
plugin.encryptrefname(ref.encode('utf-8'), poutput)
107+
return output.value.decode('utf-8')
110108

111109

112110
def decryptrefname(ref, key):
113111
'decrypt a refname'
114-
try:
115-
data = decryptdata(base64.b64decode(
116-
ref.rsplit('/', 1)[-1], b'+#'), key)
117-
assert hashlib.sha1(data[20:]).digest() == data[0:20], \
118-
'corrupted reference name'
119-
return data[20:].decode('utf-8')
120-
except ValueError:
121-
return None
112+
plugin.setkey(key)
113+
output = ctypes.create_string_buffer(math.floor(len(ref) * 3 / 4) - 20 + 1)
114+
poutput = ctypes.c_void_p(ctypes.addressof(output))
115+
plugin.decryptrefname(ref.encode('utf-8'), poutput)
116+
return output.value.decode('utf-8')
117+
118+
119+
# pylint: disable=pointless-string-statement
120+
'''
121+
mykey = b'x123456789abcdefy123456789abcdefz123456789abcdef'
122+
plugin.setkey(mykey)
123+
orig = 'o?o?o?~~~a'
124+
print(orig)
125+
encoded = encryptrefname(orig, mykey)
126+
print(encoded)
127+
print(decryptrefname(encoded, mykey))
128+
output = ctypes.create_string_buffer(100)
129+
poutput = ctypes.c_void_p(ctypes.addressof(output))
130+
outlen = ctypes.c_size_t(0)
131+
poutlen = ctypes.pointer(outlen)
132+
plugin.decryptrefname(('refs/heads/' + encoded).encode('utf-8'), poutput)
133+
print(output.value)
134+
plugin.decryptrefname(encoded.encode('utf-8'), poutput)
135+
print(output.value)
136+
137+
sys.exit(0)
138+
'''
122139

123140

124141
class CryptRepo:

incrypt-plugin.c

Lines changed: 168 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2,83 +2,214 @@
22

33
#include <stdio.h>
44
#include <string.h>
5+
#include <openssl/aes.h>
56
#include <openssl/err.h>
67
#include <openssl/evp.h>
78

8-
int encryptdata(const unsigned char* input, size_t inputlen,
9-
unsigned char* output, size_t* outputlen,
10-
const unsigned char* key);
11-
int decryptdata(const unsigned char* input, size_t inputlen,
12-
unsigned char* output, size_t* outputlen,
13-
const unsigned char* key);
14-
void cmd_main(void);
9+
#include "git-compat-util.h"
10+
#include "hash.h"
11+
12+
void setkey(const unsigned char* k);
13+
unsigned char* encryptdata(const unsigned char* input, size_t inputlen,
14+
unsigned char* output, size_t* outputlen);
15+
unsigned char* decryptdata(const unsigned char* input, size_t inputlen,
16+
unsigned char* output, size_t* outputlen);
17+
char* encryptrefname(const char* input, char* output);
18+
char* decryptrefname(const char* input, char* output);
19+
20+
static unsigned char key[48];
21+
22+
void setkey(const unsigned char* k) {
23+
memcpy(key, k, 48);
24+
}
1525

1626
static void handle_openssl_error(const char *message) {
1727
fprintf(stderr, "%s\n", message);
1828
ERR_print_errors_fp(stderr);
1929
}
2030

21-
int encryptdata(const unsigned char* input, size_t inputlen,
22-
unsigned char* output, size_t* outputlen,
23-
const unsigned char* key) {
24-
const EVP_CIPHER *cipher_type = EVP_aes_256_cbc();
25-
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
26-
int outlen;
27-
if (ctx == NULL) {
31+
struct cryptobj {
32+
EVP_CIPHER_CTX *ctx;
33+
unsigned char *output;
34+
size_t outlen;
35+
};
36+
37+
static struct cryptobj* encrypt_init(unsigned char* output) {
38+
struct cryptobj* co = malloc(sizeof(struct cryptobj));
39+
if (co == NULL)
40+
return NULL;
41+
co->ctx = EVP_CIPHER_CTX_new();
42+
if (co->ctx == NULL) {
2843
handle_openssl_error("EVP_CIPHER_CTX_new failed");
29-
return -1;
44+
free(co);
45+
return NULL;
3046
}
31-
if (EVP_EncryptInit_ex(ctx, cipher_type, NULL, key, key+32) != 1) {
47+
if (EVP_EncryptInit_ex(co->ctx, EVP_aes_256_cbc(), NULL, key, key+32) != 1) {
3248
handle_openssl_error("EVP_EncryptInit_ex failed");
33-
EVP_CIPHER_CTX_free(ctx);
34-
return -1;
49+
EVP_CIPHER_CTX_free(co->ctx);
50+
free(co);
51+
return NULL;
3552
}
36-
if (EVP_EncryptUpdate(ctx, output, &outlen, input, inputlen) != 1) {
53+
co->output = output;
54+
co->outlen = 0;
55+
return co;
56+
}
57+
58+
static struct cryptobj* encrypt_update(struct cryptobj* co,
59+
const unsigned char* input, size_t inputlen) {
60+
int outlen;
61+
if (EVP_EncryptUpdate(co->ctx, co->output + co->outlen, &outlen, input, inputlen) != 1) {
3762
handle_openssl_error("EVP_EncryptUpdate failed");
38-
EVP_CIPHER_CTX_free(ctx);
39-
return -1;
63+
EVP_CIPHER_CTX_free(co->ctx);
64+
free(co);
65+
return NULL;
4066
}
41-
*outputlen = outlen;
42-
if (EVP_EncryptFinal_ex(ctx, output + *outputlen, &outlen) != 1) {
67+
co->outlen += outlen;
68+
return co;
69+
}
70+
71+
static unsigned char* encrypt_final(struct cryptobj* co, size_t* outputlen) {
72+
int outlen;
73+
unsigned char* out = co->output;
74+
if (EVP_EncryptFinal_ex(co->ctx, co->output + co->outlen, &outlen) != 1) {
4375
handle_openssl_error("EVP_EncryptFinal_ex failed");
44-
EVP_CIPHER_CTX_free(ctx);
45-
return -1;
76+
EVP_CIPHER_CTX_free(co->ctx);
77+
free(co);
78+
return NULL;
4679
}
47-
*outputlen += outlen;
48-
EVP_CIPHER_CTX_free(ctx);
49-
return 0;
80+
co->outlen += outlen;
81+
*outputlen = co->outlen;
82+
EVP_CIPHER_CTX_free(co->ctx);
83+
free(co);
84+
return out;
5085
}
5186

52-
int decryptdata(const unsigned char* input, size_t inputlen,
53-
unsigned char* output, size_t* outputlen,
54-
const unsigned char* key) {
87+
unsigned char* encryptdata(const unsigned char* input, size_t inputlen,
88+
unsigned char* output, size_t* outputlen) {
89+
struct cryptobj* co = encrypt_init(output);
90+
encrypt_update(co, input, inputlen);
91+
return encrypt_final(co, outputlen);
92+
}
93+
94+
unsigned char* decryptdata(const unsigned char* input, size_t inputlen,
95+
unsigned char* output, size_t* outputlen) {
5596
const EVP_CIPHER *cipher_type = EVP_aes_256_cbc();
5697
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
5798
int outlen;
5899
if (ctx == NULL) {
59100
handle_openssl_error("EVP_CIPHER_CTX_new failed");
60-
return -1;
101+
return NULL;
61102
}
62103
if (EVP_DecryptInit_ex(ctx, cipher_type, NULL, key, key+32) != 1) {
63104
handle_openssl_error("EVP_DecryptInit_ex failed");
64105
EVP_CIPHER_CTX_free(ctx);
65-
return -1;
106+
return NULL;
66107
}
67108
if (EVP_DecryptUpdate(ctx, output, &outlen, input, inputlen) != 1) {
68109
handle_openssl_error("EVP_DecryptUpdate failed");
69110
EVP_CIPHER_CTX_free(ctx);
70-
return -1;
111+
return NULL;
71112
}
72113
*outputlen = outlen;
73114
if (EVP_DecryptFinal_ex(ctx, output + *outputlen, &outlen) != 1) {
74115
handle_openssl_error("EVP_DecryptFinal_ex failed");
75116
EVP_CIPHER_CTX_free(ctx);
76-
return -1;
117+
return NULL;
77118
}
78119
*outputlen += outlen;
79120
EVP_CIPHER_CTX_free(ctx);
80-
return 0;
121+
return output;
122+
}
123+
124+
static void base64encode(const unsigned char* input, size_t inputlen,
125+
unsigned char* output, size_t* outputlen) {
126+
static const char b64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+#=";
127+
size_t r = 0, w = 0;
128+
while (r < inputlen) {
129+
unsigned char b[5] = { 0, 0, 0, 0, 0 };
130+
int l = r + 3 < inputlen ? 3 : (inputlen - r);
131+
memcpy(b + 1, input + r, l);
132+
for (int i = 0; i < 4; ++i)
133+
output[w + i] =
134+
b64[(l > i - 1) ?
135+
((b[i + 0] << ((3 - i) << 1)) |
136+
(b[i + 1] >> ((1 + i) << 1))) & 0x3f :
137+
64];
138+
r += 3;
139+
w += 4;
140+
}
141+
*outputlen = w;
81142
}
82143

83-
void cmd_main(void) {
144+
static void base64decode(const unsigned char* input, size_t inputlen,
145+
unsigned char* output, size_t* outputlen) {
146+
static const unsigned char b64[] = {
147+
-1, -1, -1, 63, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, -1,
148+
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, 64, -1, -1,
149+
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
150+
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
151+
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
152+
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
153+
};
154+
unsigned char b[4] = { 0, 0, 0, 0 };
155+
size_t r = 0, w = 0;
156+
while (r < inputlen) {
157+
for (int i = 0; i < 4; ++i)
158+
b[i] = b64[input[r + i] - 32];
159+
for (int i = 0; i < 3; ++i)
160+
output[w + i] =
161+
(b[i + 0] << ((1 + i) << 1)) |
162+
(b[i + 1] >> ((2 - i) << 1));
163+
r += 4;
164+
w += 3;
165+
}
166+
*outputlen = w - (b[2] >= 64 ? 2 : (b[3] >= 64 ? 1 : 0));
167+
}
168+
169+
char* encryptrefname(const char* input, char* output) {
170+
struct git_hash_ctx c;
171+
size_t inputlen = strlen(input);
172+
size_t hashlen = hash_algos[GIT_HASH_SHA1].rawsz;
173+
unsigned char* buf1 = malloc(hashlen);
174+
unsigned char* buf2 = malloc(hashlen + inputlen + AES_BLOCK_SIZE);
175+
size_t buf2len;
176+
size_t outlen;
177+
struct cryptobj* co = encrypt_init(buf2);
178+
hash_algos[GIT_HASH_SHA1].init_fn(&c);
179+
git_hash_update(&c, input, inputlen);
180+
git_hash_final(buf1, &c);
181+
encrypt_update(co, buf1, hashlen);
182+
encrypt_update(co, (const unsigned char*)input, inputlen);
183+
encrypt_final(co, &buf2len);
184+
base64encode(buf2, buf2len, (unsigned char*)output, &outlen);
185+
output[outlen] = '\0';
186+
free(buf1);
187+
free(buf2);
188+
return output;
189+
}
190+
191+
char* decryptrefname(const char* input, char* output) {
192+
const char* data = strrchr(input, '/');
193+
size_t inputlen;
194+
unsigned char* buf1;
195+
unsigned char* buf2;
196+
size_t outlen1;
197+
size_t outlen2;
198+
size_t hashlen = hash_algos[GIT_HASH_SHA1].rawsz;
199+
data = data ? data + 1 : input;
200+
inputlen = strlen(data);
201+
buf1 = malloc(inputlen * 3 / 4);
202+
buf2 = malloc(inputlen * 3 / 4);
203+
base64decode((const unsigned char*)data, inputlen, buf1, &outlen1);
204+
decryptdata((const unsigned char*)buf1, outlen1, buf2, &outlen2);
205+
memcpy(output, buf2 + hashlen, outlen2 - hashlen);
206+
output[outlen2 - hashlen] = '\0';
207+
return output;
208+
}
209+
210+
int cmd_main(int argc, const char** argv) {
211+
(void)argv;
212+
(void)argc;
213+
(void)base64decode;
214+
return 0;
84215
}

0 commit comments

Comments
 (0)