forked from D0n9X1n/hexo-blog-encrypt
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
174 lines (142 loc) · 4.9 KB
/
index.js
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
/* global hexo, __dirname */
'use strict';
const crypto = require('crypto');
const fs = require('fs');
const path = require('path');
const log = hexo.log;
const defaultConfig = {
'abstract': 'Here\'s something encrypted, password is required to continue reading.',
'message': 'Hey, password is required here.',
'theme': 'default',
'wrong_pass_message': 'Oh, this is an invalid password. Check and try again, please.',
'wrong_hash_message': 'OOPS, these decrypted content may changed, but you can still have a look.',
'silent': false,
};
const keySalt = crypto.randomBytes(32);
const ivSalt = crypto.randomBytes(32);
// As we can't detect the wrong password with AES-CBC,
// so adding an empty tag and check it when decrption.
const knownPrefix = "<hbe-prefix></hbe-prefix>";
// disable log
var silent = false;
// use default theme
var theme = 'default';
hexo.extend.filter.register('after_post_render', (data) => {
const tagEncryptPairs = [];
let password = data.password;
let tagUsed = false;
// use a empty password to disable category encryption
if (password === "") {
return data;
}
if (hexo.config.encrypt === undefined) {
hexo.config.encrypt = [];
}
if(('encrypt' in hexo.config) && ('tags' in hexo.config.encrypt)){
hexo.config.encrypt.tags.forEach((tagObj) => {
tagEncryptPairs[tagObj.name] = tagObj.password;
});
}
if (data.tags) {
data.tags.forEach((cTag) => {
if (tagEncryptPairs.hasOwnProperty(cTag.name)) {
tagUsed = password ? tagUsed : cTag.name;
password = password || tagEncryptPairs[cTag.name];
}
});
}
if(password == undefined){
return data;
}
password = password.toString();
// make sure toc can work.
data.origin = data.content;
// Let's rock n roll
const config = Object.assign(defaultConfig, hexo.config.encrypt, data);
silent = config.silent;
theme = config.theme.trim().toLowerCase();
// deprecate the template keyword
if (config.template) {
dlog('warn', 'Looks like you use a deprecated property "template" to set up template, consider to use "theme"? See https://github.com/D0n9X1n/hexo-blog-encrypt#encrypt-theme');
}
// read theme from file
let template = fs.readFileSync(path.resolve(__dirname, `./lib/hbe.${theme}.html`)).toString();
if (tagUsed === false) {
dlog('info', `hexo-blog-encrypt: encrypting "${data.title.trim()}" based on the password configured in Front-matter with theme: ${theme}.`);
} else {
dlog('info', `hexo-blog-encrypt: encrypting "${data.title.trim()}" based on Tag: "${tagUsed}" with theme ${theme}.`);
}
data.content = knownPrefix + data.content.trim();
data.encrypt = true;
const key = crypto.pbkdf2Sync(password, keySalt, 1024, 32, 'sha256');
const iv = crypto.pbkdf2Sync(password, ivSalt, 512, 16, 'sha256');
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
const hmac = crypto.createHmac('sha256', key);
let encryptedData = cipher.update(data.content, 'utf8', 'hex');
hmac.update(data.content, 'utf8');
encryptedData += cipher.final('hex');
const hmacDigest = hmac.digest('hex');
data.content = template.replace(/{{hbeEncryptedData}}/g, encryptedData)
.replace(/{{hbeHmacDigest}}/g, hmacDigest)
.replace(/{{hbeWrongPassMessage}}/g, config.wrong_pass_message)
.replace(/{{hbeWrongHashMessage}}/g, config.wrong_hash_message)
.replace(/{{hbeMessage}}/g, config.message)
.replace(/{{hbeKeySalt}}/g, keySalt.toString('hex'))
.replace(/{{hbeIvSalt}}/g, ivSalt.toString('hex'));
data.content += `<script data-pjax src="${hexo.config.root}lib/hbe.js"></script><link href="${hexo.config.root}css/hbe.style.css" rel="stylesheet" type="text/css">`;
data.excerpt = data.more = config.abstract;
return data;
}, 1000);
hexo.extend.generator.register('hexo-blog-encrypt', () => [
{
'data': () => fs.createReadStream(path.resolve(__dirname, `./lib/hbe.style.css`)),
'path': `css/hbe.style.css`,
},
{
'data': () => fs.createReadStream(path.resolve(__dirname, './lib/hbe.js')),
'path': 'lib/hbe.js',
},
]);
// log function
function dlog(level, x) {
switch (level) {
case 'warn':
log.warn(x);
break;
case 'info':
default:
if (silent) {
return;
}
log.info(x);
}
}
// Utils functions
function textToArray(s) {
var i = s.length;
var n = 0;
var ba = new Array()
for (var j = 0; j < i;) {
var c = s.codePointAt(j);
if (c < 128) {
ba[n++] = c;
j++;
} else if ((c > 127) && (c < 2048)) {
ba[n++] = (c >> 6) | 192;
ba[n++] = (c & 63) | 128;
j++;
} else if ((c > 2047) && (c < 65536)) {
ba[n++] = (c >> 12) | 224;
ba[n++] = ((c >> 6) & 63) | 128;
ba[n++] = (c & 63) | 128;
j++;
} else {
ba[n++] = (c >> 18) | 240;
ba[n++] = ((c >> 12) & 63) | 128;
ba[n++] = ((c >> 6) & 63) | 128;
ba[n++] = (c & 63) | 128;
j += 2;
}
}
return new Uint8Array(ba);
}