Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

crypto: bring module into modern age

Introduce 'buffer' encoding, allow returning and giving buffers as
arguments of 'crypto' routines.

Fix #3278
  • Loading branch information...
commit 63ff449d87e23b5e3d475da960135c1a2fd0ed58 1 parent 3301c90
@indutny indutny authored
View
51 doc/api/crypto.markdown
@@ -64,14 +64,14 @@ Returned by `crypto.createHash`.
### hash.update(data, [input_encoding])
Updates the hash content with the given `data`, the encoding of which is given
-in `input_encoding` and can be `'utf8'`, `'ascii'` or `'binary'`.
+in `input_encoding` and can be `'buffer'`, `'utf8'`, `'ascii'` or `'binary'`.
Defaults to `'binary'`.
This can be called many times with new data as it is streamed.
### hash.digest([encoding])
Calculates the digest of all of the passed data to be hashed.
-The `encoding` can be `'hex'`, `'binary'` or `'base64'`.
+The `encoding` can be `'buffer'`, `'hex'`, `'binary'` or `'base64'`.
Defaults to `'binary'`.
Note: `hash` object can not be used after `digest()` method been called.
@@ -98,7 +98,7 @@ This can be called many times with new data as it is streamed.
### hmac.digest([encoding])
Calculates the digest of all of the passed data to the hmac.
-The `encoding` can be `'hex'`, `'binary'` or `'base64'`.
+The `encoding` can be `'buffer'`, `'hex'`, `'binary'` or `'base64'`.
Defaults to `'binary'`.
Note: `hmac` object can not be used after `digest()` method been called.
@@ -134,18 +134,18 @@ Returned by `crypto.createCipher` and `crypto.createCipheriv`.
### cipher.update(data, [input_encoding], [output_encoding])
Updates the cipher with `data`, the encoding of which is given in
-`input_encoding` and can be `'utf8'`, `'ascii'` or `'binary'`.
+`input_encoding` and can be `'buffer'`, `'utf8'`, `'ascii'` or `'binary'`.
Defaults to `'binary'`.
The `output_encoding` specifies the output format of the enciphered data,
-and can be `'binary'`, `'base64'` or `'hex'`. Defaults to `'binary'`.
+and can be `'buffer'`, `'binary'`, `'base64'` or `'hex'`. Defaults to `'binary'`.
Returns the enciphered contents, and can be called many times with new data as it is streamed.
### cipher.final([output_encoding])
Returns any remaining enciphered contents, with `output_encoding` being one of:
-`'binary'`, `'base64'` or `'hex'`. Defaults to `'binary'`.
+`'buffer'`, `'binary'`, `'base64'` or `'hex'`. Defaults to `'binary'`.
Note: `cipher` object can not be used after `final()` method been called.
@@ -174,16 +174,18 @@ Returned by `crypto.createDecipher` and `crypto.createDecipheriv`.
### decipher.update(data, [input_encoding], [output_encoding])
-Updates the decipher with `data`, which is encoded in `'binary'`, `'base64'`
-or `'hex'`. Defaults to `'binary'`.
+Updates the decipher with `data`, which is encoded in `'buffer'`, `'binary'`,
+`'base64'` or `'hex'`. Defaults to `'binary'`.
The `output_decoding` specifies in what format to return the deciphered
-plaintext: `'binary'`, `'ascii'` or `'utf8'`. Defaults to `'binary'`.
+plaintext: `'buffer'`, `'binary'`, `'ascii'` or `'utf8'`.
+Defaults to `'binary'`.
### decipher.final([output_encoding])
Returns any remaining plaintext which is deciphered,
-with `output_encoding` being one of: `'binary'`, `'ascii'` or `'utf8'`.
+with `output_encoding` being one of: `'buffer'`, `'binary'`, `'ascii'` or
+`'utf8'`.
Defaults to `'binary'`.
Note: `decipher` object can not be used after `final()` method been called.
@@ -216,8 +218,8 @@ This can be called many times with new data as it is streamed.
Calculates the signature on all the updated data passed through the signer.
`private_key` is a string containing the PEM encoded private key for signing.
-Returns the signature in `output_format` which can be `'binary'`, `'hex'` or
-`'base64'`. Defaults to `'binary'`.
+Returns the signature in `output_format` which can be `'buffer'`, `'binary'`,
+`'hex'` or `'base64'`. Defaults to `'binary'`.
Note: `signer` object can not be used after `sign()` method been called.
@@ -242,8 +244,8 @@ This can be called many times with new data as it is streamed.
Verifies the signed data by using the `object` and `signature`. `object` is a
string containing a PEM encoded object, which can be one of RSA public key,
DSA public key, or X.509 certificate. `signature` is the previously calculated
-signature for the data, in the `signature_format` which can be `'binary'`,
-`'hex'` or `'base64'`. Defaults to `'binary'`.
+signature for the data, in the `signature_format` which can be `'buffer'`,
+`'binary'`, `'hex'` or `'base64'`. Defaults to `'binary'`.
Returns true or false depending on the validity of the signature for the data and public key.
@@ -257,7 +259,8 @@ given bit length. The generator used is `2`.
## crypto.createDiffieHellman(prime, [encoding])
Creates a Diffie-Hellman key exchange object using the supplied prime. The
-generator used is `2`. Encoding can be `'binary'`, `'hex'`, or `'base64'`.
+generator used is `2`. Encoding can be `'buffer'`, `'binary'`, `'hex'`, or
+`'base64'`.
Defaults to `'binary'`.
## Class: DiffieHellman
@@ -278,19 +281,19 @@ Defaults to `'binary'`.
Computes the shared secret using `other_public_key` as the other party's
public key and returns the computed shared secret. Supplied key is
interpreted using specified `input_encoding`, and secret is encoded using
-specified `output_encoding`. Encodings can be `'binary'`, `'hex'`, or
-`'base64'`. The input encoding defaults to `'binary'`.
+specified `output_encoding`. Encodings can be `'buffer'`, `'binary'`, `'hex'`,
+or `'base64'`. The input encoding defaults to `'binary'`.
If no output encoding is given, the input encoding is used as output encoding.
### diffieHellman.getPrime([encoding])
Returns the Diffie-Hellman prime in the specified encoding, which can be
-`'binary'`, `'hex'`, or `'base64'`. Defaults to `'binary'`.
+`'buffer'`, `'binary'`, `'hex'`, or `'base64'`. Defaults to `'binary'`.
### diffieHellman.getGenerator([encoding])
Returns the Diffie-Hellman prime in the specified encoding, which can be
-`'binary'`, `'hex'`, or `'base64'`. Defaults to `'binary'`.
+`'buffer'`, `'binary'`, `'hex'`, or `'base64'`. Defaults to `'binary'`.
### diffieHellman.getPublicKey([encoding])
@@ -300,17 +303,17 @@ be `'binary'`, `'hex'`, or `'base64'`. Defaults to `'binary'`.
### diffieHellman.getPrivateKey([encoding])
Returns the Diffie-Hellman private key in the specified encoding, which can
-be `'binary'`, `'hex'`, or `'base64'`. Defaults to `'binary'`.
+be `'buffer'`, `'binary'`, `'hex'`, or `'base64'`. Defaults to `'binary'`.
### diffieHellman.setPublicKey(public_key, [encoding])
-Sets the Diffie-Hellman public key. Key encoding can be `'binary'`, `'hex'`,
-or `'base64'`. Defaults to `'binary'`.
+Sets the Diffie-Hellman public key. Key encoding can be `'buffer', ``'binary'`,
+`'hex'` or `'base64'`. Defaults to `'binary'`.
### diffieHellman.setPrivateKey(public_key, [encoding])
-Sets the Diffie-Hellman private key. Key encoding can be `'binary'`, `'hex'`,
-or `'base64'`. Defaults to `'binary'`.
+Sets the Diffie-Hellman private key. Key encoding can be `'buffer'`, `'binary'`,
+`'hex'` or `'base64'`. Defaults to `'binary'`.
## crypto.getDiffieHellman(group_name)
View
12 src/node.cc
@@ -1067,6 +1067,8 @@ enum encoding ParseEncoding(Handle<Value> encoding_v, enum encoding _default) {
return UCS2;
} else if (strcasecmp(*encoding, "binary") == 0) {
return BINARY;
+ } else if (strcasecmp(*encoding, "buffer") == 0) {
+ return BUFFER;
} else if (strcasecmp(*encoding, "hex") == 0) {
return HEX;
} else if (strcasecmp(*encoding, "raw") == 0) {
@@ -1089,6 +1091,11 @@ enum encoding ParseEncoding(Handle<Value> encoding_v, enum encoding _default) {
Local<Value> Encode(const void *buf, size_t len, enum encoding encoding) {
HandleScope scope;
+ if (encoding == BUFFER) {
+ return scope.Close(
+ Buffer::New(static_cast<const char*>(buf), len)->handle_);
+ }
+
if (!len) return scope.Close(String::Empty());
if (encoding == BINARY) {
@@ -1119,7 +1126,7 @@ ssize_t DecodeBytes(v8::Handle<v8::Value> val, enum encoding encoding) {
return -1;
}
- if (encoding == BINARY && Buffer::HasInstance(val)) {
+ if ((encoding == BUFFER || encoding == BINARY) && Buffer::HasInstance(val)) {
return Buffer::Length(val->ToObject());
}
@@ -1158,7 +1165,8 @@ ssize_t DecodeWrite(char *buf,
bool is_buffer = Buffer::HasInstance(val);
- if (is_buffer && encoding == BINARY) { // fast path, copy buffer data
+ if (is_buffer && (encoding == BINARY || encoding == BUFFER)) {
+ // fast path, copy buffer data
const char* data = Buffer::Data(val.As<Object>());
size_t size = Buffer::Length(val.As<Object>());
size_t len = size < buflen ? size : buflen;
View
2  src/node.h
@@ -127,7 +127,7 @@ void SetPrototypeMethod(target_t target,
#define NODE_SET_METHOD node::SetMethod
#define NODE_SET_PROTOTYPE_METHOD node::SetPrototypeMethod
-enum encoding {ASCII, UTF8, BASE64, UCS2, BINARY, HEX};
+enum encoding {ASCII, UTF8, BASE64, UCS2, BINARY, HEX, BUFFER};
enum encoding ParseEncoding(v8::Handle<v8::Value> encoding_v,
enum encoding _default = BINARY);
NODE_EXTERN void FatalException(v8::TryCatch &try_catch);
View
205 src/node_crypto.cc
@@ -1621,7 +1621,8 @@ Handle<Value> Connection::SetSession(const Arguments& args) {
Connection *ss = Connection::Unwrap(args);
- if (args.Length() < 1 || !args[0]->IsString()) {
+ if (args.Length() < 1 ||
+ (!args[0]->IsString() && !Buffer::HasInstance(args[0]))) {
Local<Value> exception = Exception::TypeError(String::New("Bad argument"));
return ThrowException(exception);
}
@@ -2379,53 +2380,49 @@ class Cipher : public ObjectWrap {
}
Local<Value> outString;
- if (out_len==0) {
- outString=String::New("");
- } else {
- char* out_hexdigest;
- int out_hex_len;
- enum encoding enc = ParseEncoding(args[2], BINARY);
- if (enc == HEX) {
- // Hex encoding
- HexEncode(out, out_len, &out_hexdigest, &out_hex_len);
- outString = Encode(out_hexdigest, out_hex_len, BINARY);
- delete [] out_hexdigest;
- } else if (enc == BASE64) {
- // Base64 encoding
- // Check to see if we need to add in previous base64 overhang
- if (cipher->incomplete_base64!=NULL){
- unsigned char* complete_base64 = new unsigned char[out_len+cipher->incomplete_base64_len+1];
- memcpy(complete_base64, cipher->incomplete_base64, cipher->incomplete_base64_len);
- memcpy(&complete_base64[cipher->incomplete_base64_len], out, out_len);
- delete [] out;
-
- delete [] cipher->incomplete_base64;
- cipher->incomplete_base64=NULL;
-
- out=complete_base64;
- out_len += cipher->incomplete_base64_len;
- }
+ char* out_hexdigest;
+ int out_hex_len;
+ enum encoding out_enc = ParseEncoding(args[2], BINARY);
+ if (out_enc == HEX) {
+ // Hex encoding
+ HexEncode(out, out_len, &out_hexdigest, &out_hex_len);
+ outString = Encode(out_hexdigest, out_hex_len, BINARY);
+ delete [] out_hexdigest;
+ } else if (out_enc == BASE64) {
+ // Base64 encoding
+ // Check to see if we need to add in previous base64 overhang
+ if (cipher->incomplete_base64!=NULL){
+ unsigned char* complete_base64 = new unsigned char[out_len+cipher->incomplete_base64_len+1];
+ memcpy(complete_base64, cipher->incomplete_base64, cipher->incomplete_base64_len);
+ memcpy(&complete_base64[cipher->incomplete_base64_len], out, out_len);
+ delete [] out;
- // Check to see if we need to trim base64 stream
- if (out_len%3!=0){
- cipher->incomplete_base64_len = out_len%3;
- cipher->incomplete_base64 = new char[cipher->incomplete_base64_len+1];
- memcpy(cipher->incomplete_base64,
- &out[out_len-cipher->incomplete_base64_len],
- cipher->incomplete_base64_len);
- out_len -= cipher->incomplete_base64_len;
- out[out_len]=0;
- }
+ delete [] cipher->incomplete_base64;
+ cipher->incomplete_base64=NULL;
- base64(out, out_len, &out_hexdigest, &out_hex_len);
- outString = Encode(out_hexdigest, out_hex_len, BINARY);
- delete [] out_hexdigest;
- } else if (enc == BINARY) {
- outString = Encode(out, out_len, BINARY);
- } else {
- fprintf(stderr, "node-crypto : Cipher .update encoding "
- "can be binary, hex or base64\n");
+ out=complete_base64;
+ out_len += cipher->incomplete_base64_len;
}
+
+ // Check to see if we need to trim base64 stream
+ if (out_len%3!=0){
+ cipher->incomplete_base64_len = out_len%3;
+ cipher->incomplete_base64 = new char[cipher->incomplete_base64_len+1];
+ memcpy(cipher->incomplete_base64,
+ &out[out_len-cipher->incomplete_base64_len],
+ cipher->incomplete_base64_len);
+ out_len -= cipher->incomplete_base64_len;
+ out[out_len]=0;
+ }
+
+ base64(out, out_len, &out_hexdigest, &out_hex_len);
+ outString = Encode(out_hexdigest, out_hex_len, BINARY);
+ delete [] out_hexdigest;
+ } else if (out_enc == BINARY || out_enc == BUFFER) {
+ outString = Encode(out, out_len, out_enc);
+ } else {
+ fprintf(stderr, "node-crypto : Cipher .update encoding "
+ "can be binary, buffer, hex or base64\n");
}
if (out) delete [] out;
@@ -2461,12 +2458,11 @@ class Cipher : public ObjectWrap {
if (out_len == 0 || r == 0) {
// out_value always get allocated.
delete[] out_value;
+ out_value = NULL;
if (r == 0) {
Local<Value> exception = Exception::TypeError(
String::New("CipherFinal fail"));
return ThrowException(exception);
- } else {
- return scope.Close(String::New(""));
}
}
@@ -2493,11 +2489,11 @@ class Cipher : public ObjectWrap {
base64(out_value, out_len, &out_hexdigest, &out_hex_len);
outString = Encode(out_hexdigest, out_hex_len, BINARY);
delete [] out_hexdigest;
- } else if (enc == BINARY) {
- outString = Encode(out_value, out_len, BINARY);
+ } else if (enc == BINARY || enc == BUFFER) {
+ outString = Encode(out_value, out_len, enc);
} else {
fprintf(stderr, "node-crypto : Cipher .final encoding "
- "can be binary, hex or base64\n");
+ "can be binary, buffer, hex or base64\n");
}
delete [] out_value;
@@ -2826,12 +2822,12 @@ class Decipher : public ObjectWrap {
len = ciphertext_len;
alloc_buf = true;
- } else if (enc == BINARY) {
+ } else if (enc == BINARY || enc == BUFFER) {
// Binary - do nothing
} else {
fprintf(stderr, "node-crypto : Decipher .update encoding "
- "can be binary, hex or base64\n");
+ "can be binary, buffer, hex or base64\n");
}
unsigned char *out=0;
@@ -2845,35 +2841,31 @@ class Decipher : public ObjectWrap {
}
Local<Value> outString;
- if (out_len==0) {
- outString=String::New("");
- } else {
- enum encoding enc = ParseEncoding(args[2], BINARY);
- if (enc == UTF8) {
- // See if we have any overhang from last utf8 partial ending
- if (cipher->incomplete_utf8!=NULL) {
- char* complete_out = new char[cipher->incomplete_utf8_len + out_len];
- memcpy(complete_out, cipher->incomplete_utf8, cipher->incomplete_utf8_len);
- memcpy((char *)complete_out+cipher->incomplete_utf8_len, out, out_len);
- delete [] out;
-
- delete [] cipher->incomplete_utf8;
- cipher->incomplete_utf8 = NULL;
-
- out = (unsigned char*)complete_out;
- out_len += cipher->incomplete_utf8_len;
- }
- // Check to see if we have a complete utf8 stream
- int utf8_len = LengthWithoutIncompleteUtf8((char *)out, out_len);
- if (utf8_len<out_len) { // We have an incomplete ut8 ending
- cipher->incomplete_utf8_len = out_len-utf8_len;
- cipher->incomplete_utf8 = new unsigned char[cipher->incomplete_utf8_len+1];
- memcpy(cipher->incomplete_utf8, &out[utf8_len], cipher->incomplete_utf8_len);
- }
- outString = Encode(out, utf8_len, enc);
- } else {
- outString = Encode(out, out_len, enc);
+ enum encoding out_enc = ParseEncoding(args[2], BINARY);
+ if (out_enc == UTF8) {
+ // See if we have any overhang from last utf8 partial ending
+ if (cipher->incomplete_utf8!=NULL) {
+ char* complete_out = new char[cipher->incomplete_utf8_len + out_len];
+ memcpy(complete_out, cipher->incomplete_utf8, cipher->incomplete_utf8_len);
+ memcpy((char *)complete_out+cipher->incomplete_utf8_len, out, out_len);
+ delete [] out;
+
+ delete [] cipher->incomplete_utf8;
+ cipher->incomplete_utf8 = NULL;
+
+ out = (unsigned char*)complete_out;
+ out_len += cipher->incomplete_utf8_len;
+ }
+ // Check to see if we have a complete utf8 stream
+ int utf8_len = LengthWithoutIncompleteUtf8((char *)out, out_len);
+ if (utf8_len<out_len) { // We have an incomplete ut8 ending
+ cipher->incomplete_utf8_len = out_len-utf8_len;
+ cipher->incomplete_utf8 = new unsigned char[cipher->incomplete_utf8_len+1];
+ memcpy(cipher->incomplete_utf8, &out[utf8_len], cipher->incomplete_utf8_len);
}
+ outString = Encode(out, utf8_len, out_enc);
+ } else {
+ outString = Encode(out, out_len, out_enc);
}
if (out) delete [] out;
@@ -2908,19 +2900,18 @@ class Decipher : public ObjectWrap {
if (out_len == 0 || r == 0) {
delete [] out_value; // allocated even if out_len == 0
+ out_value = NULL;
if (r == 0) {
Local<Value> exception = Exception::TypeError(
String::New("DecipherFinal fail"));
return ThrowException(exception);
- } else {
- return scope.Close(String::New(""));
}
}
if (args.Length() == 0 || !args[0]->IsString()) {
outString = Encode(out_value, out_len, BINARY);
} else {
- enum encoding enc = ParseEncoding(args[0]);
+ enum encoding enc = ParseEncoding(args[0], BINARY);
if (enc == UTF8) {
// See if we have any overhang from last utf8 partial ending
if (cipher->incomplete_utf8!=NULL) {
@@ -3118,8 +3109,9 @@ class Hmac : public ObjectWrap {
Local<Value> outString;
int r = hmac->HmacDigest(&md_value, &md_len);
- if (md_len == 0 || r == 0) {
- return scope.Close(String::New(""));
+ if (r == 0) {
+ md_value = NULL;
+ md_len = 0;
}
enum encoding enc = ParseEncoding(args[0], BINARY);
@@ -3132,11 +3124,11 @@ class Hmac : public ObjectWrap {
base64(md_value, md_len, &md_hexdigest, &md_hex_len);
outString = Encode(md_hexdigest, md_hex_len, BINARY);
delete [] md_hexdigest;
- } else if (enc == BINARY) {
- outString = Encode(md_value, md_len, BINARY);
+ } else if (enc == BINARY || enc == BUFFER) {
+ outString = Encode(md_value, md_len, enc);
} else {
fprintf(stderr, "node-crypto : Hmac .digest encoding "
- "can be binary, hex or base64\n");
+ "can be binary, buffer, hex or base64\n");
}
delete [] md_value;
return scope.Close(outString);
@@ -3267,10 +3259,6 @@ class Hash : public ObjectWrap {
EVP_MD_CTX_cleanup(&hash->mdctx);
hash->initialised_ = false;
- if (md_len == 0) {
- return scope.Close(String::New(""));
- }
-
Local<Value> outString;
enum encoding enc = ParseEncoding(args[0], BINARY);
@@ -3287,11 +3275,11 @@ class Hash : public ObjectWrap {
base64(md_value, md_len, &md_hexdigest, &md_hex_len);
outString = Encode(md_hexdigest, md_hex_len, BINARY);
delete [] md_hexdigest;
- } else if (enc == BINARY) {
- outString = Encode(md_value, md_len, BINARY);
+ } else if (enc == BINARY || enc == BUFFER) {
+ outString = Encode(md_value, md_len, enc);
} else {
fprintf(stderr, "node-crypto : Hash .digest encoding "
- "can be binary, hex or base64\n");
+ "can be binary, buffer, hex or base64\n");
}
return scope.Close(outString);
@@ -3471,14 +3459,13 @@ class Sign : public ObjectWrap {
assert(written == len);
int r = sign->SignFinal(&md_value, &md_len, buf, len);
+ if (r == 0) {
+ md_value = NULL;
+ md_len = r;
+ }
delete [] buf;
- if (md_len == 0 || r == 0) {
- delete [] md_value;
- return scope.Close(String::New(""));
- }
-
enum encoding enc = ParseEncoding(args[1], BINARY);
if (enc == HEX) {
// Hex encoding
@@ -3489,12 +3476,12 @@ class Sign : public ObjectWrap {
base64(md_value, md_len, &md_hexdigest, &md_hex_len);
outString = Encode(md_hexdigest, md_hex_len, BINARY);
delete [] md_hexdigest;
- } else if (enc == BINARY) {
- outString = Encode(md_value, md_len, BINARY);
+ } else if (enc == BINARY || enc == BUFFER) {
+ outString = Encode(md_value, md_len, enc);
} else {
outString = String::New("");
fprintf(stderr, "node-crypto : Sign .sign encoding "
- "can be binary, hex or base64\n");
+ "can be binary, buffer, hex or base64\n");
}
delete [] md_value;
@@ -3741,11 +3728,11 @@ class Verify : public ObjectWrap {
unbase64(hbuf, hlen, (char **)&dbuf, &dlen);
r = verify->VerifyFinal(kbuf, klen, dbuf, dlen);
delete [] dbuf;
- } else if (enc == BINARY) {
+ } else if (enc == BINARY || enc == BUFFER) {
r = verify->VerifyFinal(kbuf, klen, hbuf, hlen);
} else {
fprintf(stderr, "node-crypto : Verify .verify encoding "
- "can be binary, hex or base64\n");
+ "can be binary, buffer, hex or base64\n");
}
delete [] kbuf;
@@ -4301,7 +4288,7 @@ class DiffieHellman : public ObjectWrap {
// Binary - do nothing
} else {
fprintf(stderr, "node-crypto : Diffie-Hellman parameter encoding "
- "can be binary, hex or base64\n");
+ "can be binary, buffer, hex or base64\n");
}
if (retbuf != 0) {
@@ -4331,11 +4318,11 @@ class DiffieHellman : public ObjectWrap {
base64(reinterpret_cast<unsigned char*>(buf), len, &retbuf, &retlen);
outString = Encode(retbuf, retlen, BINARY);
delete [] retbuf;
- } else if (enc == BINARY) {
- outString = Encode(buf, len, BINARY);
+ } else if (enc == BINARY || enc == BUFFER) {
+ outString = Encode(buf, len, enc);
} else {
fprintf(stderr, "node-crypto : Diffie-Hellman parameter encoding "
- "can be binary, hex or base64\n");
+ "can be binary, buffer, hex or base64\n");
}
return scope.Close(outString);
View
4 test/simple/test-crypto-padding-aes256.js
@@ -37,13 +37,13 @@ function aes256(decipherFinal) {
function encrypt(val, pad) {
var c = crypto.createCipheriv('aes256', key, iv);
c.setAutoPadding(pad);
- return c.update(val, 'utf8') + c.final('binary');
+ return c.update(val, 'utf8', 'binary') + c.final('binary');
}
function decrypt(val, pad) {
var c = crypto.createDecipheriv('aes256', key, iv);
c.setAutoPadding(pad);
- return c.update(val, 'binary') + c[decipherFinal]('utf8');
+ return c.update(val, 'binary', 'binary') + c[decipherFinal]('utf8');
}
// echo 0123456789abcdef0123456789abcdef \
View
41 test/simple/test-crypto.js
@@ -369,6 +369,7 @@ var a0 = crypto.createHash('sha1').update('Test123').digest('hex');
var a1 = crypto.createHash('md5').update('Test123').digest('binary');
var a2 = crypto.createHash('sha256').update('Test123').digest('base64');
var a3 = crypto.createHash('sha512').update('Test123').digest(); // binary
+var a4 = crypto.createHash('sha1').update('Test123').digest('buffer');
assert.equal(a0, '8308651804facb7b9af8ffc53a33a22d6a1c8ac2', 'Test SHA1');
assert.equal(a1, 'h\u00ea\u00cb\u0097\u00d8o\fF!\u00fa+\u000e\u0017\u00ca' +
@@ -381,6 +382,9 @@ assert.equal(a3, '\u00c1(4\u00f1\u0003\u001fd\u0097!O\'\u00d4C/&Qz\u00d4' +
'\u00d7\u00d6\u00a2\u00a8\u0085\u00e3<\u0083\u009c\u0093' +
'\u00c2\u0006\u00da0\u00a1\u00879(G\u00ed\'',
'Test SHA512 as assumed binary');
+assert.deepEqual(a4,
+ new Buffer('8308651804facb7b9af8ffc53a33a22d6a1c8ac2', 'hex'),
+ 'Test SHA1');
// Test multiple updates to same hash
var h1 = crypto.createHash('sha1').update('Test123').digest('hex');
@@ -424,6 +428,15 @@ var verified = crypto.createVerify('RSA-SHA256')
.verify(certPem, s2); // binary
assert.strictEqual(verified, true, 'sign and verify (binary)');
+var s3 = crypto.createSign('RSA-SHA1')
+ .update('Test123')
+ .sign(keyPem, 'buffer');
+var verified = crypto.createVerify('RSA-SHA1')
+ .update('Test')
+ .update('123')
+ .verify(certPem, s3);
+assert.strictEqual(verified, true, 'sign and verify (buffer)');
+
function testCipher1(key) {
// Test encryption and decryption
@@ -484,6 +497,24 @@ function testCipher3(key, iv) {
}
+function testCipher4(key, iv) {
+ // Test encyrption and decryption with explicit key and iv
+ var plaintext =
+ '32|RmVZZkFUVmpRRkp0TmJaUm56ZU9qcnJkaXNNWVNpTTU*|iXmckfRWZBGWWELw' +
+ 'eCBsThSsfUHLeRe0KCsK8ooHgxie0zOINpXxfZi/oNG7uq9JWFVCk70gfzQH8ZUJ' +
+ 'jAfaFg**';
+ var cipher = crypto.createCipheriv('des-ede3-cbc', key, iv);
+ var ciph = cipher.update(plaintext, 'utf8', 'buffer');
+ ciph = Buffer.concat([ciph, cipher.final('buffer')]);
+
+ var decipher = crypto.createDecipheriv('des-ede3-cbc', key, iv);
+ var txt = decipher.update(ciph, 'buffer', 'utf8');
+ txt += decipher.final('utf8');
+
+ assert.equal(txt, plaintext, 'encryption and decryption with key and iv');
+}
+
+
testCipher1('MySecretKey123');
testCipher1(new Buffer('MySecretKey123'));
@@ -495,6 +526,8 @@ testCipher3('0123456789abcd0123456789', new Buffer('12345678'));
testCipher3(new Buffer('0123456789abcd0123456789'), '12345678');
testCipher3(new Buffer('0123456789abcd0123456789'), new Buffer('12345678'));
+testCipher4(new Buffer('0123456789abcd0123456789'), new Buffer('12345678'));
+
// update() should only take buffers / strings
assert.throws(function() {
@@ -505,18 +538,18 @@ assert.throws(function() {
// Test Diffie-Hellman with two parties sharing a secret,
// using various encodings as we go along
var dh1 = crypto.createDiffieHellman(256);
-var p1 = dh1.getPrime('base64');
+var p1 = dh1.getPrime('buffer');
var dh2 = crypto.createDiffieHellman(p1, 'base64');
var key1 = dh1.generateKeys();
var key2 = dh2.generateKeys('hex');
var secret1 = dh1.computeSecret(key2, 'hex', 'base64');
-var secret2 = dh2.computeSecret(key1, 'binary', 'base64');
+var secret2 = dh2.computeSecret(key1, 'binary', 'buffer');
-assert.equal(secret1, secret2);
+assert.equal(secret1, secret2.toString('base64'));
// Create "another dh1" using generated keys from dh1,
// and compute secret again
-var dh3 = crypto.createDiffieHellman(p1, 'base64');
+var dh3 = crypto.createDiffieHellman(p1, 'buffer');
var privkey1 = dh1.getPrivateKey();
dh3.setPublicKey(key1);
dh3.setPrivateKey(privkey1);

14 comments on commit 63ff449

@mscdex
Collaborator

Can we make the default be 'buffer' instead of 'binary' if no encoding is given and the argument is a Buffer?

@langpavel

@mscdex I think it might break backward compatibility.

@mscdex
Collaborator

That's fine.... this is in 0.9/master already because it's an API change :-)

@TooTallNate

+1

I was under the impression that this "buffer encoding" was going to be internal, and not exposed in the docs. Returning a Buffer instance should be the default, just like all the other Node core APIs IMO.

@langpavel

@TooTallNate @mscdex:
All crypto is stable

3 - Stable The API has proven satisfactory, but cleanup in the underlying code may cause minor changes.
Backwards-compatibility is guaranteed.

This doesn't mean that we can change stable API between major releases, or I'm wrong?

By changing default behaviour you break lot of modules. (need investigation)

Note that there is more features required from crypto e.g. streams.

One posibility is entirely new module (something like crypto.2) which will respect all the new goods.

@mscdex
Collaborator

IMHO if "stable" here means that this particular change cannot be made, then I think it was a mistake to label it as "stable", especially when Buffer support wasn't even added until just now (which is crazy).

Also as @TooTallNate mentioned, it's better to have Buffer as a default encoding to more closely match encoding behavior elsewhere in node. The less gotchas the better.

@langpavel

@mscdex Absolutely agree, but...

  • Step one - deprecate binary encoding, which is now implicit in this API. This will come in #3279 (@isaacs?)
  • Step two - cut out binary encoding entirely and introduce Buffer as implicit, deprecate other encodings in crypto module.

but step two must come some time later, giving maintainers of packages a chance to update

@mscdex
Collaborator

As much as I agree that the 'binary' encoding is not very efficient/elegant/whatever, it's very nice to have available because you can use native string functions to find bytes, perform manipulations, etc.

IMHO since Buffer does not have these same kinds of methods available (unless core members would be willing to merge in something like node-buffertools), we should not remove 'binary' encoding support.

Also, I'm not so sure that deprecating additional/other encodings in crypto is a good idea (making a Buffer the only allowed input/output?). Especially if we're really talking about just crypto, since that would introduce more gotchas if encodings can be used in similar situations elsewhere in node.

@mscdex
Collaborator

@richardhoffman that's like telling people to just convert binary string results back to a Buffer when using crypto functions. Sure you can do that, but why would you want to do it?

@langpavel

@mscdex Where input or output has no encoding by nature - just bytes - there shouldn't be string input or output.
Speaking in C++ types, void * and char * are different. No reason to reinterpret_cast them in javascript.

Node Buffer is not part of standart nor V8, that means pull requests can be accepted if reasonable.

If you want text, you can get if from buffer by Buffer#toString(encoding, start, end) and you should choose proper one or 'hex'

@yi

+1 to @mscdex and @TooTallNate : it's better to have Buffer as a default encoding to more closely match encoding behavior elsewhere in node.

To @richardhoffman: Why would you need to modify a crypto buffer?
Because:
1. computers talk in binary, not talk in human language
2. String in JavaScript is UTF friendly, which is handy to write program logic, but is a waster in data processing.

@isaacs
Owner

So, here's a plan. @indutny et al, please let me know if you think this is sane:

  1. Take this patch, basically as-is. binary remains the default, and we add buffer, noting that it is kludgey and weird.
  2. Reduce the crypto stability index to 2: Unstable. Add a paragraph in the docs noting that the binary encoding will cease to be the default in a future version.
  3. Release 0.10.
  4. In 0.11.0, make buffer the default, and bump the stability back up to 3: Stable.
@TooTallNate
@langpavel

@isaacs SGTM too

Please sign in to comment.
Something went wrong with that request. Please try again.