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
|
#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{
// Envelope format: [AES encrypted data | RSA encrypted AES key | length of AES key]
// The encrypted AES key is in big-endian format, as is the length tag. The length tag
// is an unsigned 16-bit integer.
bool safeKey(const string &key){
//checks against keys 0 and 1, because they don't undergo change in RSA enryption
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));
string payload(AES::encrypt(data,aeskey,AES::AES_256_CBC));
Bigint rsadata;
for(int i=0;i<(int)aeskey.size();i++){
if(i!=0)rsadata<<=8; //we see aeskey as being in big-endian format
rsadata+=(uint8_t)aeskey[i];
}
Bigint res(RSA::encrypt(rsadata,pubkey));
vector<uint8_t> bytes; //bytes in little-endian order (so, reversed!)
while(res!=0){
bytes.push_back(res.lowdigits()&0xff);
res>>=8;
}
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){
if(data.size()<2)throw invalid_argument("Envelope data length invalid");
//This double cast is necessary because of sign extension.
int encrkeylen=((uint16_t)(uint8_t)data[data.size()-2]<<8)+(uint8_t)data.back();
assert(encrkeylen<(1<<16));
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];
}
Bigint rsadata;
for(int i=0;i<encrkeylen;i++){
if(i!=0)rsadata<<=8; //encrkey is also in big-endian
rsadata+=(uint8_t)encrkey[i];
}
Bigint res(RSA::decrypt(rsadata,privkey));
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]; //save in decrkey in big-endian order again
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");
}
}
}
|