-
Notifications
You must be signed in to change notification settings - Fork 2
/
signature.js
182 lines (168 loc) · 6.39 KB
/
signature.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
175
176
177
178
179
180
181
182
const crypto = require('crypto');
/**
* The signature module contains a small set of basic utilities for generating
* and verifying signatures.
* @module easy-crypto/signature
*/
/**
* Generate signatures for a message using a private key.
* @since 0.1.0
* @function sign
* @param {Object|string|Buffer|KeyObject} privateKey The private key to be used for signing the message.
* @param {string} algorithm The hashing algorithm to be used.
* @param {Data} message The message to be signed.
* @param {string} inputEncoding The encoding of the `message`. If `message` is a string and no value is provided, an encoding of `'utf8'` will be enforced. Ignored if message is a `Buffer`, `TypedArray` or `DataView`.
* @param {string} outputEncoding The encoding of the output signature. If provided a `string` is returned, otherwise a `Buffer` is returned.
* @returns {string|Buffer} The generated signature for the provided message.
* @static
*/
function sign(privateKey, algorithm, message, inputEncoding, outputEncoding) {
const signFunc = crypto.createSign(algorithm);
signFunc.update(message, inputEncoding);
signFunc.end();
return signFunc.sign(privateKey, outputEncoding);
}
/**
* Generate signatures for a message encapsulated in a stream using a private key.
* @since 0.1.0
* @async
* @function signStream
* @param {Object|string|Buffer|KeyObject} privateKey The private key to be used for signing the message.
* @param {string} algorithm The hashing algorithm to be used.
* @param {ReadableStream<Data>} input The input stream containing the message to be signed.
* @param {string} inputEncoding The encoding of the `message`. If `message` is a string and no value is provided, an encoding of `'utf8'` will be enforced. Ignored if message is a `Buffer`, `TypedArray` or `DataView`.
* @param {string} outputEncoding The encoding of the output signature. If provided a `string` is returned, otherwise a `Buffer` is returned.
* @returns {Promise<string|Buffer>} The generated signature for the provided message.
* @static
*/
function signStream(
privateKey,
algorithm,
input,
inputEncoding,
outputEncoding
) {
return new Promise((resolve, reject) => {
let data;
let wasBuffer;
const dataHandler = chunk => {
const isBuffer = Buffer.isBuffer(chunk);
if ((!isBuffer && wasBuffer) || (isBuffer && wasBuffer === false)) {
reject(new Error('Inconsistent data.'));
input.removeListener('data', dataHandler);
return;
}
if (isBuffer) {
wasBuffer = true;
chunk = chunk.toString('utf8');
} else {
wasBuffer = false;
}
if (data === undefined) {
data = chunk;
} else {
data += chunk;
}
};
input.on('data', dataHandler);
input.on('end', () => {
if (data === undefined) {
reject(new Error('No data to sign.'));
} else {
resolve(
sign(privateKey, algorithm, data, inputEncoding, outputEncoding)
);
}
});
});
}
/**
* Verify if a signature is valid for a given message using the corresponding public key.
* @since 0.1.0
* @function verify
* @param {Object|string|Buffer|KeyObject} publicKey The public key to be used for verifying the signature.
* @param {string} algorithm The hashing algorithm to be used.
* @param {Data} message The message for which the signature has been generated.
* @param {string|Buffer} signature The signature to be verified.
* @param {string} inputEncoding The encoding of the `message`. If `message` is a string and no value is provided, an encoding of `'utf8'` will be enforced. Ignored if message is a `Buffer`, `TypedArray` or `DataView`.
* @param {string} signatureEncoding The encoding of the provided `signature`. If a signatureEncoding is specified, the signature is expected to be a string; otherwise signature is expected to be a Buffer, TypedArray, or DataView.
* @returns {boolean} Wether the signature was valid or not.
* @static
*/
function verify(
publicKey,
algorithm,
message,
signature,
inputEncoding,
signatureEncoding
) {
const verifyFunc = crypto.createVerify(algorithm);
verifyFunc.update(message, inputEncoding);
verifyFunc.end();
return verifyFunc.verify(publicKey, signature, signatureEncoding);
}
/**
* Verify if a signature is valid for a given message encapsulated in a stream using the corresponding public key.
* @since 0.1.0
* @async
* @function verifyStream
* @param {Object|string|Buffer|KeyObject} publicKey The public key to be used for verifying the signature.
* @param {string} algorithm The hashing algorithm to be used.
* @param {ReadableStream<Data>} input The stream containing the message for which the signature has been generated.
* @param {string|Buffer} signature The signature to be verified.
* @param {string} inputEncoding The encoding of the `message`. If `message` is a string and no value is provided, an encoding of `'utf8'` will be enforced. Ignored if message is a `Buffer`, `TypedArray` or `DataView`.
* @param {string} signatureEncoding The encoding of the provided `signature`. If a signatureEncoding is specified, the signature is expected to be a string; otherwise signature is expected to be a Buffer, TypedArray, or DataView.
* @returns {Promise<boolean>} Wether the signature was valid or not.
* @static
*/
function verifyStream(
publicKey,
algorithm,
input,
signature,
inputEncoding,
signatureEncoding
) {
return new Promise((resolve, reject) => {
let data;
let wasBuffer;
const dataHandler = chunk => {
const isBuffer = Buffer.isBuffer(chunk);
if ((!isBuffer && wasBuffer) || (isBuffer && wasBuffer === false)) {
reject(new Error('Inconsistent data.'));
input.removeListener('data', dataHandler);
return;
}
if (isBuffer) {
wasBuffer = true;
chunk = chunk.toString('utf8');
} else {
wasBuffer = false;
}
if (data === undefined) {
data = chunk;
} else {
data += chunk;
}
};
input.on('data', dataHandler);
input.on('end', () => {
if (data === undefined) {
reject(new Error('No data to sign.'));
} else {
resolve(
verify(
publicKey,
algorithm,
data,
signature,
inputEncoding,
signatureEncoding
)
);
}
});
});
}
module.exports = { sign, signStream, verify, verifyStream };