aboutsummaryrefslogtreecommitdiff
path: root/envelope.cpp
blob: 497135800e4a19b602f2fd2d83f8665b7b9a148f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#include <stdexcept>
#include <cassert>
#include "aes.h"
#include "envelope.h"
#include "rng.h"

#undef DEBUG

#ifdef DEBUG
#include "base64.h"
#endif

using namespace std;

namespace Envelope{

	bool safeKey(const string &key){
		//checks against keys 0 and 1, because they don't undergo change in RSA
		int i;
		for(i=0;i<(int)key.size();i++)if(key[i]!=0)break;
		if(i==(int)key.size())return false; //key is 0
		if(i==(int)key.size()-1&&key[i]==1)return false; //key is 1
		return true; //fine
	}

	string encrypt(const string &data,const RSA::Key &pubkey){
		const int keylen=8; //256-bit
		CryptoRng crng;
		string aeskey(4*keylen,'\0');
		do {
			for(int i=0;i<keylen;i++)*(uint32_t*)&aeskey[4*i]=crng.get();
		} while(!safeKey(aeskey));
#ifdef DEBUG
		cerr<<"decrkey="<<Base64::encode(aeskey)<<endl;
#endif
		
		string payload(AES::encrypt(data,aeskey,AES::AES_256_CBC));
#ifdef DEBUG
		cerr<<"payload="<<Base64::encode(payload)<<endl;
#endif
		Bigint rsadata;
		for(int i=0;i<(int)aeskey.size();i++){
			if(i!=0)rsadata<<=8;
			rsadata+=(uint8_t)aeskey[i];
		}
#ifdef DEBUG
		cerr<<"rsadata="<<rsadata<<endl;
		cerr<<"pubkey = {"<<pubkey.mod<<" , "<<pubkey.exp<<'}'<<endl;
#endif
		Bigint res(RSA::encrypt(rsadata,pubkey));
#ifdef DEBUG
		cerr<<"rsaencr="<<res<<endl;
#endif

		vector<uint8_t> bytes; //bytes in little-endian order (so, reverse!)
		while(res!=0){
			bytes.push_back(res.lowdigits()&0xff);
			res>>=8;
		}
#ifdef DEBUG
		cerr<<"encrkey="<<Base64::encode(string(bytes.rbegin(),bytes.rend()))<<endl;
#endif

		payload.reserve(payload.size()+bytes.size()+2);
		for(int i=bytes.size()-1;i>=0;i--)payload.push_back(bytes[i]); //append in big-endian order again
		payload.push_back(bytes.size()>>8);
		payload.push_back((uint8_t)bytes.size()&0xff);

		return payload;
	}

	string decrypt(const string &data,const RSA::Key &privkey){
#ifdef DEBUG
		cerr<<"=== DECRYPT ==="<<endl;
#endif
		if(data.size()<2)throw invalid_argument("Envelope data length invalid");
		int encrkeylen=((uint16_t)(uint8_t)data[data.size()-2]<<8)+(uint8_t)data.back();
		assert(encrkeylen<(1<<16));
#ifdef DEBUG
		cerr<<"encrkeylen="<<encrkeylen<<endl;
#endif
		if((int)data.size()<encrkeylen+2)throw invalid_argument("Envelope key format invalid");

		string encrkey(encrkeylen,'\0'); //in big-endian
		for(int i=0;i<encrkeylen;i++){
			encrkey[i]=data[data.size()-2-encrkeylen+i];
		}
#ifdef DEBUG
		cerr<<"encrkey="<<Base64::encode(encrkey)<<endl;
#endif

		Bigint rsadata;
		for(int i=0;i<encrkeylen;i++){
			if(i!=0)rsadata<<=8;
			rsadata+=(uint8_t)encrkey[i];
		}
#ifdef DEBUG
		cerr<<"rsaencr="<<rsadata<<endl;
		cerr<<"privkey = {"<<privkey.mod<<" , "<<privkey.exp<<'}'<<endl;
#endif
		Bigint res(RSA::decrypt(rsadata,privkey));
#ifdef DEBUG
		cerr<<"rsadata="<<res<<endl;
#endif

		vector<uint8_t> bytes; //bytes in little-endian order
		for(int i=0;i<32;i++){ //32 is the number of bytes in a 256-bit AES key (256/8=32)
			bytes.push_back(res.lowdigits()&0xff);
			res>>=8;
		}
		if(res!=0){
			throw invalid_argument("Envelope RSA private key incorrect");
		}
		string decrkey(bytes.size(),'\0');
		for(int i=0;i<(int)bytes.size();i++)decrkey[bytes.size()-1-i]=bytes[i];
#ifdef DEBUG
		cerr<<"decrkey="<<Base64::encode(decrkey)<<endl;
#endif

#ifdef DEBUG
		cerr<<"payload="<<Base64::encode(data.substr(0,data.size()-2-encrkeylen))<<endl;
#endif
		try {
			return AES::decrypt(data.substr(0,data.size()-2-encrkeylen),decrkey,AES::AES_256_CBC);
		} catch(invalid_argument){
			throw invalid_argument("Envelope RSA private key incorrect");
		}
	}

}