diff --git a/doc/api/crypto.markdown b/doc/api/crypto.markdown index 87b9ac37827..102c2be0cc2 100644 --- a/doc/api/crypto.markdown +++ b/doc/api/crypto.markdown @@ -63,15 +63,17 @@ 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'`. +Updates the hash content with the given `data`, which can be a string or a +buffer. If `data` is a string, its encoding is given in `input_encoding` +and can be `'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 `'hex'`, `'binary'`, `'base64'` or `null` for returning +a buffer. Defaults to `'binary'`. Note: `hash` object can not be used after `digest()` method been called. @@ -98,7 +100,8 @@ 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 `'hex'`, `'binary'`, `'base64'` or `null` for returning +a buffer. Defaults to `'binary'`. Note: `hmac` object can not be used after `digest()` method been called. @@ -133,19 +136,23 @@ 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'`. +Updates the cipher with `data`, which can be a string or a buffer. If +`data` is a string its encoding is given in `input_encoding` and can be +`'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'`. +The `output_encoding` specifies the output format of the enciphered data, and +can be `'binary'`, `'base64'`, `'hex'` or `null`. If `null`, the enciphered +data will be returned as a buffer. Defaults to `'binary'`. -Returns the enciphered contents, and can be called many times with new data as it is streamed. +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'`. +Returns any remaining enciphered contents, with `output_encoding` being one +of: `'binary'`, `'base64'`, `'hex'` or `null`. If `null`, the result will +be a buffer. Defaults to `'binary'`. Note: `cipher` object can not be used after `final()` method been called. @@ -174,17 +181,19 @@ 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 can be a string or a buffer. If +`data` is a string its encoding is given in `input_encoding` and can be +`'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: `'binary'`, `'ascii'`, `'utf8'` or `null`. If `null`, the plaintext +will be returned as a buffer. Defaults to `'binary'`. ### decipher.final([output_encoding]) -Returns any remaining plaintext which is deciphered, -with `output_encoding` being one of: `'binary'`, `'ascii'` or `'utf8'`. -Defaults to `'binary'`. +Returns any remaining plaintext which is deciphered, with `output_encoding` +being one of: `'binary'`, `'ascii'`, `'utf8'` or `null`. If `null`, the +plaintext will be returned as a buffer. Defaults to `'binary'`. Note: `decipher` object can not be used after `final()` method been called. @@ -211,13 +220,14 @@ Returned by `crypto.createSign`. Updates the signer object with data. This can be called many times with new data as it is streamed. -### signer.sign(private_key, [output_format]) +### signer.sign(private_key, [output_encoding]) 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_encoding` which can be `'binary'`, `'hex'`, +`'base64'` or `null`. If `null`, the signature will be returned as a +buffer. Defaults to `'binary'`. Note: `signer` object can not be used after `sign()` method been called. @@ -237,13 +247,13 @@ Returned by `crypto.createVerify`. Updates the verifier object with data. This can be called many times with new data as it is streamed. -### verifier.verify(object, signature, [signature_format]) +### verifier.verify(object, signature, [encoding]) 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; it can be a buffer or a string in the `encoding` +which can be `'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 +267,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`. `prime` can be a buffer or string encoded in +`'binary'`, `'hex'`, or `'base64'`. Defaults to `'binary'`. ## Class: DiffieHellman @@ -270,47 +281,53 @@ Returned by `crypto.createDiffieHellman`. Generates private and public Diffie-Hellman key values, and returns the public key in the specified encoding. This key should be transferred to the -other party. Encoding can be `'binary'`, `'hex'`, or `'base64'`. -Defaults to `'binary'`. +other party. Encoding can be `'binary'`, `'hex'`, `'base64'` or `null`. If +`null`, the public key will be returned as a buffer. Defaults to `'binary'`. ### diffieHellman.computeSecret(other_public_key, [input_encoding], [output_encoding]) -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'`. -If no output encoding is given, the input encoding is used as output encoding. +Computes the shared secret using `other_public_key` as the other +party's public key and returns the computed shared secret. Supplied +key can be a string or a buffer. If it is a string it is interpreted +using specified `input_encoding`. Secret is encoded using specified +`output_encoding`. Encodings can be `'binary'`, `'hex'`, `'base64'` or `null` +for buffers. 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'`. +Returns the Diffie-Hellman prime in the specified encoding, which can +be `'binary'`, `'hex'`, `'base64'` or `null`. If `null`, a buffer is +returned. 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'`. +Returns the Diffie-Hellman generator in the specified encoding, which can +be `'binary'`, `'hex'`, `'base64'` or `null`. If `null`, a buffer is +returned. Defaults to `'binary'`. ### diffieHellman.getPublicKey([encoding]) Returns the Diffie-Hellman public key in the specified encoding, which can -be `'binary'`, `'hex'`, or `'base64'`. Defaults to `'binary'`. +be `'binary'`, `'hex'`, `'base64'` or `null`. If `null`, a buffer is +returned. 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 `'binary'`, `'hex'`, `'base64'` or `null`. If `null`, a buffer is +returned. 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 can be a buffer or a string +encoded in `'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 can be a buffer or a string +encoded in `'binary'`, `'hex'`, or `'base64'`. Defaults to `'binary'`. ## crypto.getDiffieHellman(group_name) diff --git a/src/node_crypto.cc b/src/node_crypto.cc index b09fbbb1dc2..bbe2dfb2d59 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -1975,6 +1975,33 @@ int local_EVP_DecryptFinal_ex(EVP_CIPHER_CTX *ctx, } +class BinaryData { + public: + char* buf; + ssize_t buf_len; + + BinaryData(const Local arg, const enum encoding enc, ssize_t len) + : buf_len(len), + clean(false) { + if (Buffer::HasInstance(arg)) { + Local buffer = arg->ToObject(); + buf = Buffer::Data(buffer); + buf_len = Buffer::Length(buffer); + } else { + buf = new char[len]; + ssize_t written = DecodeWrite(buf, len, arg, enc); + assert(written == len); + clean = true; + } + } + + ~BinaryData() { + if (clean) delete [] buf; + } + private: + bool clean; +}; + class Cipher : public ObjectWrap { public: static void Initialize (v8::Handle target) { @@ -2108,15 +2135,11 @@ class Cipher : public ObjectWrap { return ThrowException(exception); } - char* key_buf = new char[key_buf_len]; - ssize_t key_written = DecodeWrite(key_buf, key_buf_len, args[1], BINARY); - assert(key_written == key_buf_len); + BinaryData key = BinaryData(args[1], BINARY, key_buf_len); String::Utf8Value cipherType(args[0]); - bool r = cipher->CipherInit(*cipherType, key_buf, key_buf_len); - - delete [] key_buf; + bool r = cipher->CipherInit(*cipherType, key.buf, key.buf_len); if (!r) { return ThrowException(Exception::Error(String::New("CipherInit error"))); @@ -2158,20 +2181,14 @@ class Cipher : public ObjectWrap { return ThrowException(exception); } - char* key_buf = new char[key_len]; - ssize_t key_written = DecodeWrite(key_buf, key_len, args[1], BINARY); - assert(key_written == key_len); - - char* iv_buf = new char[iv_len]; - ssize_t iv_written = DecodeWrite(iv_buf, iv_len, args[2], BINARY); - assert(iv_written == iv_len); + BinaryData key = BinaryData(args[1], BINARY, key_len); + BinaryData iv = BinaryData(args[2], BINARY, iv_len); String::Utf8Value cipherType(args[0]); - bool r = cipher->CipherInitIv(*cipherType, key_buf,key_len,iv_buf,iv_len); - - delete [] key_buf; - delete [] iv_buf; + bool r = cipher->CipherInitIv(*cipherType, + key.buf, key.buf_len, + iv.buf, iv.buf_len); if (!r) { return ThrowException(Exception::Error(String::New("CipherInitIv error"))); @@ -2195,21 +2212,11 @@ class Cipher : public ObjectWrap { return ThrowException(exception); } - unsigned char *out=0; - int out_len=0, r; - if (Buffer::HasInstance(args[0])) { - Local buffer_obj = args[0]->ToObject(); - char *buffer_data = Buffer::Data(buffer_obj); - size_t buffer_length = Buffer::Length(buffer_obj); + unsigned char *out = 0; + int out_len = 0; + BinaryData data = BinaryData(args[0], enc, len); - r = cipher->CipherUpdate(buffer_data, buffer_length, &out, &out_len); - } else { - char* buf = new char[len]; - ssize_t written = DecodeWrite(buf, len, args[0], enc); - assert(written == len); - r = cipher->CipherUpdate(buf, len,&out,&out_len); - delete [] buf; - } + int r = cipher->CipherUpdate(data.buf, data.buf_len, &out, &out_len); if (!r) { delete [] out; @@ -2217,9 +2224,11 @@ class Cipher : public ObjectWrap { return ThrowException(exception); } - Local outString; - if (out_len==0) { - outString=String::New(""); + Handle output; + if (args[2]->IsNull()) { + output = Buffer::New((char*) out, out_len)->handle_; + } else if (out_len==0) { + output = String::New(""); } else { char* out_hexdigest; int out_hex_len; @@ -2227,7 +2236,7 @@ class Cipher : public ObjectWrap { if (enc == HEX) { // Hex encoding HexEncode(out, out_len, &out_hexdigest, &out_hex_len); - outString = Encode(out_hexdigest, out_hex_len, BINARY); + output = Encode(out_hexdigest, out_hex_len, BINARY); delete [] out_hexdigest; } else if (enc == BASE64) { // Base64 encoding @@ -2257,10 +2266,10 @@ class Cipher : public ObjectWrap { } base64(out, out_len, &out_hexdigest, &out_hex_len); - outString = Encode(out_hexdigest, out_hex_len, BINARY); + output = Encode(out_hexdigest, out_hex_len, BINARY); delete [] out_hexdigest; } else if (enc == BINARY) { - outString = Encode(out, out_len, BINARY); + output = Encode(out, out_len, BINARY); } else { fprintf(stderr, "node-crypto : Cipher .update encoding " "can be binary, hex or base64\n"); @@ -2269,7 +2278,7 @@ class Cipher : public ObjectWrap { if (out) delete [] out; - return scope.Close(outString); + return scope.Close(output); } static Handle SetAutoPadding(const Arguments& args) { @@ -2290,7 +2299,7 @@ class Cipher : public ObjectWrap { int out_len = -1; char* out_hexdigest; int out_hex_len; - Local outString ; + Handle output ; int r = cipher->CipherFinal(&out_value, &out_len); @@ -2309,38 +2318,42 @@ class Cipher : public ObjectWrap { } } - enum encoding enc = ParseEncoding(args[0], BINARY); - if (enc == HEX) { - // Hex encoding - HexEncode(out_value, out_len, &out_hexdigest, &out_hex_len); - outString = Encode(out_hexdigest, out_hex_len, BINARY); - delete [] out_hexdigest; - } else if (enc == BASE64) { - // 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_value, out_len); - delete [] out_value; - - delete [] cipher->incomplete_base64; - cipher->incomplete_base64=NULL; - - out_value=complete_base64; - out_len += cipher->incomplete_base64_len; - } - 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); + if (args[0]->IsNull()) { + output = Buffer::New((char*) out_value, out_len)->handle_; } else { - fprintf(stderr, "node-crypto : Cipher .final encoding " - "can be binary, hex or base64\n"); + enum encoding enc = ParseEncoding(args[0], BINARY); + if (enc == HEX) { + // Hex encoding + HexEncode(out_value, out_len, &out_hexdigest, &out_hex_len); + output = Encode(out_hexdigest, out_hex_len, BINARY); + delete [] out_hexdigest; + } else if (enc == BASE64) { + // 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_value, out_len); + delete [] out_value; + + delete [] cipher->incomplete_base64; + cipher->incomplete_base64=NULL; + + out_value=complete_base64; + out_len += cipher->incomplete_base64_len; + } + base64(out_value, out_len, &out_hexdigest, &out_hex_len); + output = Encode(out_hexdigest, out_hex_len, BINARY); + delete [] out_hexdigest; + } else if (enc == BINARY) { + output = Encode(out_value, out_len, BINARY); + } else { + fprintf(stderr, "node-crypto : Cipher .final encoding " + "can be binary, hex or base64\n"); + } } delete [] out_value; - return scope.Close(outString); + return scope.Close(output); } Cipher () : ObjectWrap () @@ -2613,22 +2626,12 @@ class Decipher : public ObjectWrap { "node`DecodeBytes() failed"))); } - char* buf; // if alloc_buf then buf must be deleted later bool alloc_buf = false; - if (Buffer::HasInstance(args[0])) { - Local buffer_obj = args[0]->ToObject(); - char *buffer_data = Buffer::Data(buffer_obj); - size_t buffer_length = Buffer::Length(buffer_obj); - buf = buffer_data; - len = buffer_length; - } else { - alloc_buf = true; - buf = new char[len]; - ssize_t written = DecodeWrite(buf, len, args[0], BINARY); - assert(written == len); - } + BinaryData data = BinaryData(args[0], BINARY, len); + char* buf = data.buf; + len = data.buf_len; char* ciphertext; int ciphertext_len; @@ -2641,10 +2644,7 @@ class Decipher : public ObjectWrap { char* complete_hex = new char[len+2]; memcpy(complete_hex, &cipher->incomplete_hex, 1); memcpy(complete_hex+1, buf, len); - if (alloc_buf) { - delete [] buf; - alloc_buf = false; - } + alloc_buf = true; buf = complete_hex; len += 1; } @@ -2666,9 +2666,6 @@ class Decipher : public ObjectWrap { } else if (enc == BASE64) { unbase64((unsigned char*)buf, len, (char **)&ciphertext, &ciphertext_len); - if (alloc_buf) { - delete [] buf; - } buf = ciphertext; len = ciphertext_len; alloc_buf = true; @@ -2691,9 +2688,11 @@ class Decipher : public ObjectWrap { return ThrowException(exception); } - Local outString; - if (out_len==0) { - outString=String::New(""); + Handle output; + if (args[2]->IsNull()) { + output = Buffer::New((char*) out, out_len)->handle_; + } else if (out_len==0) { + output = String::New(""); } else { enum encoding enc = ParseEncoding(args[2], BINARY); if (enc == UTF8) { @@ -2717,16 +2716,16 @@ class Decipher : public ObjectWrap { 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); + output = Encode(out, utf8_len, enc); } else { - outString = Encode(out, out_len, enc); + output = Encode(out, out_len, enc); } } if (out) delete [] out; if (alloc_buf) delete [] buf; - return scope.Close(outString); + return scope.Close(output); } @@ -2747,7 +2746,7 @@ class Decipher : public ObjectWrap { unsigned char* out_value = NULL; int out_len = -1; - Local outString; + Handle output; int r = cipher->DecipherFinal(&out_value, &out_len); @@ -2765,8 +2764,10 @@ class Decipher : public ObjectWrap { } } - if (args.Length() == 0 || !args[0]->IsString()) { - outString = Encode(out_value, out_len, BINARY); + if (args[0]->IsNull()) { + output = Buffer::New((char*) out_value, out_len)->handle_; + } else if (args.Length() == 0 || !args[0]->IsString()) { + output = Encode(out_value, out_len, BINARY); } else { enum encoding enc = ParseEncoding(args[0]); if (enc == UTF8) { @@ -2779,17 +2780,17 @@ class Decipher : public ObjectWrap { delete [] cipher->incomplete_utf8; cipher->incomplete_utf8=NULL; - outString = Encode(complete_out, cipher->incomplete_utf8_len+out_len, enc); + output = Encode(complete_out, cipher->incomplete_utf8_len+out_len, enc); delete [] complete_out; } else { - outString = Encode(out_value, out_len, enc); + output = Encode(out_value, out_len, enc); } } else { - outString = Encode(out_value, out_len, enc); + output = Encode(out_value, out_len, enc); } } delete [] out_value; - return scope.Close(outString); + return scope.Close(output); } Decipher () : ObjectWrap () { @@ -2891,23 +2892,8 @@ class Hmac : public ObjectWrap { String::Utf8Value hashType(args[0]); - bool r; - - if( Buffer::HasInstance(args[1])) { - Local buffer_obj = args[1]->ToObject(); - char* buffer_data = Buffer::Data(buffer_obj); - size_t buffer_length = Buffer::Length(buffer_obj); - - r = hmac->HmacInit(*hashType, buffer_data, buffer_length); - } else { - char* buf = new char[len]; - ssize_t written = DecodeWrite(buf, len, args[1], BINARY); - assert(written == len); - - r = hmac->HmacInit(*hashType, buf, len); - - delete [] buf; - } + BinaryData key = BinaryData(args[1], BINARY, len); + bool r = hmac->HmacInit(*hashType, key.buf, key.buf_len); if (!r) { return ThrowException(Exception::Error(String::New("hmac error"))); @@ -2930,21 +2916,8 @@ class Hmac : public ObjectWrap { return ThrowException(exception); } - int r; - - if( Buffer::HasInstance(args[0])) { - Local buffer_obj = args[0]->ToObject(); - char *buffer_data = Buffer::Data(buffer_obj); - size_t buffer_length = Buffer::Length(buffer_obj); - - r = hmac->HmacUpdate(buffer_data, buffer_length); - } else { - char* buf = new char[len]; - ssize_t written = DecodeWrite(buf, len, args[0], enc); - assert(written == len); - r = hmac->HmacUpdate(buf, len); - delete [] buf; - } + BinaryData data = BinaryData(args[0], enc, len); + int r = hmac->HmacUpdate(data.buf, data.buf_len); if (!r) { Local exception = Exception::TypeError(String::New("HmacUpdate fail")); @@ -2963,31 +2936,35 @@ class Hmac : public ObjectWrap { unsigned int md_len = 0; char* md_hexdigest; int md_hex_len; - Local outString; + Handle output; int r = hmac->HmacDigest(&md_value, &md_len); if (md_len == 0 || r == 0) { return scope.Close(String::New("")); } - enum encoding enc = ParseEncoding(args[0], BINARY); - if (enc == HEX) { - // Hex encoding - HexEncode(md_value, md_len, &md_hexdigest, &md_hex_len); - outString = Encode(md_hexdigest, md_hex_len, BINARY); - delete [] md_hexdigest; - } else if (enc == BASE64) { - 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); + if (args[0]->IsNull()) { + output = Buffer::New((char*) md_value, md_len)->handle_; } else { - fprintf(stderr, "node-crypto : Hmac .digest encoding " - "can be binary, hex or base64\n"); + enum encoding enc = ParseEncoding(args[0], BINARY); + if (enc == HEX) { + // Hex encoding + HexEncode(md_value, md_len, &md_hexdigest, &md_hex_len); + output = Encode(md_hexdigest, md_hex_len, BINARY); + delete [] md_hexdigest; + } else if (enc == BASE64) { + base64(md_value, md_len, &md_hexdigest, &md_hex_len); + output = Encode(md_hexdigest, md_hex_len, BINARY); + delete [] md_hexdigest; + } else if (enc == BINARY) { + output = Encode(md_value, md_len, BINARY); + } else { + fprintf(stderr, "node-crypto : Hmac .digest encoding " + "can be binary, hex or base64\n"); + } } delete [] md_value; - return scope.Close(outString); + return scope.Close(output); } Hmac () : ObjectWrap () { @@ -3076,20 +3053,8 @@ class Hash : public ObjectWrap { return ThrowException(exception); } - int r; - - if (Buffer::HasInstance(args[0])) { - Local buffer_obj = args[0]->ToObject(); - char *buffer_data = Buffer::Data(buffer_obj); - size_t buffer_length = Buffer::Length(buffer_obj); - r = hash->HashUpdate(buffer_data, buffer_length); - } else { - char* buf = new char[len]; - ssize_t written = DecodeWrite(buf, len, args[0], enc); - assert(written == len); - r = hash->HashUpdate(buf, len); - delete[] buf; - } + BinaryData data = BinaryData(args[0], enc, len); + int r = hash->HashUpdate(data.buf, data.buf_len); if (!r) { Local exception = Exception::TypeError(String::New("HashUpdate fail")); @@ -3119,30 +3084,34 @@ class Hash : public ObjectWrap { return scope.Close(String::New("")); } - Local outString; + Handle output; - enum encoding enc = ParseEncoding(args[0], BINARY); - if (enc == HEX) { - // Hex encoding - char* md_hexdigest; - int md_hex_len; - HexEncode(md_value, md_len, &md_hexdigest, &md_hex_len); - outString = Encode(md_hexdigest, md_hex_len, BINARY); - delete [] md_hexdigest; - } else if (enc == BASE64) { - char* md_hexdigest; - int md_hex_len; - 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); + if (args[0]->IsNull()) { + output = Buffer::New((char*) md_value, md_len)->handle_; } else { - fprintf(stderr, "node-crypto : Hash .digest encoding " - "can be binary, hex or base64\n"); + enum encoding enc = ParseEncoding(args[0], BINARY); + if (enc == HEX) { + // Hex encoding + char* md_hexdigest; + int md_hex_len; + HexEncode(md_value, md_len, &md_hexdigest, &md_hex_len); + output = Encode(md_hexdigest, md_hex_len, BINARY); + delete [] md_hexdigest; + } else if (enc == BASE64) { + char* md_hexdigest; + int md_hex_len; + base64(md_value, md_len, &md_hexdigest, &md_hex_len); + output = Encode(md_hexdigest, md_hex_len, BINARY); + delete [] md_hexdigest; + } else if (enc == BINARY) { + output = Encode(md_value, md_len, BINARY); + } else { + fprintf(stderr, "node-crypto : Hash .digest encoding " + "can be binary, hex or base64\n"); + } } - return scope.Close(outString); + return scope.Close(output); } Hash () : ObjectWrap () { @@ -3267,21 +3236,8 @@ class Sign : public ObjectWrap { return ThrowException(exception); } - int r; - - if (Buffer::HasInstance(args[0])) { - Local buffer_obj = args[0]->ToObject(); - char *buffer_data = Buffer::Data(buffer_obj); - size_t buffer_length = Buffer::Length(buffer_obj); - - r = sign->SignUpdate(buffer_data, buffer_length); - } else { - char* buf = new char[len]; - ssize_t written = DecodeWrite(buf, len, args[0], enc); - assert(written == len); - r = sign->SignUpdate(buf, len); - delete [] buf; - } + BinaryData data = BinaryData(args[0], enc, len); + int r = sign->SignUpdate(data.buf, data.buf_len); if (!r) { Local exception = Exception::TypeError(String::New("SignUpdate fail")); @@ -3300,7 +3256,7 @@ class Sign : public ObjectWrap { unsigned int md_len; char* md_hexdigest; int md_hex_len; - Local outString; + Handle output; md_len = 8192; // Maximum key size is 8192 bits md_value = new unsigned char[md_len]; @@ -3327,26 +3283,30 @@ class Sign : public ObjectWrap { return scope.Close(String::New("")); } - enum encoding enc = ParseEncoding(args[1], BINARY); - if (enc == HEX) { - // Hex encoding - HexEncode(md_value, md_len, &md_hexdigest, &md_hex_len); - outString = Encode(md_hexdigest, md_hex_len, BINARY); - delete [] md_hexdigest; - } else if (enc == BASE64) { - 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); + if (args[1]->IsNull()) { + output = Buffer::New((char*) md_value, md_len)->handle_; } else { - outString = String::New(""); - fprintf(stderr, "node-crypto : Sign .sign encoding " - "can be binary, hex or base64\n"); + enum encoding enc = ParseEncoding(args[1], BINARY); + if (enc == HEX) { + // Hex encoding + HexEncode(md_value, md_len, &md_hexdigest, &md_hex_len); + output = Encode(md_hexdigest, md_hex_len, BINARY); + delete [] md_hexdigest; + } else if (enc == BASE64) { + base64(md_value, md_len, &md_hexdigest, &md_hex_len); + output = Encode(md_hexdigest, md_hex_len, BINARY); + delete [] md_hexdigest; + } else if (enc == BINARY) { + output = Encode(md_value, md_len, BINARY); + } else { + output = String::New(""); + fprintf(stderr, "node-crypto : Sign .sign encoding " + "can be binary, hex or base64\n"); + } } delete [] md_value; - return scope.Close(outString); + return scope.Close(output); } Sign () : ObjectWrap () { @@ -3519,21 +3479,8 @@ class Verify : public ObjectWrap { return ThrowException(exception); } - int r; - - if(Buffer::HasInstance(args[0])) { - Local buffer_obj = args[0]->ToObject(); - char *buffer_data = Buffer::Data(buffer_obj); - size_t buffer_length = Buffer::Length(buffer_obj); - - r = verify->VerifyUpdate(buffer_data, buffer_length); - } else { - char* buf = new char[len]; - ssize_t written = DecodeWrite(buf, len, args[0], enc); - assert(written == len); - r = verify->VerifyUpdate(buf, len); - delete [] buf; - } + BinaryData data = BinaryData(args[0], enc, len); + int r = verify->VerifyUpdate(data.buf, data.buf_len); if (!r) { Local exception = Exception::TypeError(String::New("VerifyUpdate fail")); @@ -3570,34 +3517,32 @@ class Verify : public ObjectWrap { return ThrowException(exception); } - unsigned char* hbuf = new unsigned char[hlen]; - ssize_t hwritten = DecodeWrite((char *)hbuf, hlen, args[1], BINARY); - assert(hwritten == hlen); unsigned char* dbuf; int dlen; + BinaryData signature = BinaryData(args[1], BINARY, hlen); + int r=-1; enum encoding enc = ParseEncoding(args[2], BINARY); if (enc == HEX) { // Hex encoding - HexDecode(hbuf, hlen, (char **)&dbuf, &dlen); + HexDecode((unsigned char*) signature.buf, signature.buf_len, (char **)&dbuf, &dlen); r = verify->VerifyFinal(kbuf, klen, dbuf, dlen); delete [] dbuf; } else if (enc == BASE64) { // Base64 encoding - unbase64(hbuf, hlen, (char **)&dbuf, &dlen); + unbase64((unsigned char*) signature.buf, signature.buf_len, (char **)&dbuf, &dlen); r = verify->VerifyFinal(kbuf, klen, dbuf, dlen); delete [] dbuf; - } else if (enc == BINARY) { - r = verify->VerifyFinal(kbuf, klen, hbuf, hlen); + } else if (enc == BINARY || Buffer::HasInstance(args[1])) { + r = verify->VerifyFinal(kbuf, klen, (unsigned char*) signature.buf, signature.buf_len); } else { fprintf(stderr, "node-crypto : Verify .verify encoding " "can be binary, hex or base64\n"); } delete [] kbuf; - delete [] hbuf; return Boolean::New(r && r != -1); } @@ -3778,21 +3723,23 @@ class DiffieHellman : public ObjectWrap { String::New("Key generation failed"))); } - Local outString; + Handle output; int dataSize = BN_num_bytes(diffieHellman->dh->pub_key); char* data = new char[dataSize]; BN_bn2bin(diffieHellman->dh->pub_key, reinterpret_cast(data)); - if (args.Length() > 0 && args[0]->IsString()) { - outString = EncodeWithEncoding(args[0], data, dataSize); + if (args[0]->IsNull()) { + output = Buffer::New((char*) data, dataSize)->handle_; + } else if (args.Length() > 0 && args[0]->IsString()) { + output = EncodeWithEncoding(args[0], data, dataSize); } else { - outString = Encode(data, dataSize, BINARY); + output = Encode(data, dataSize, BINARY); } delete[] data; - return scope.Close(outString); + return scope.Close(output); } static Handle GetPrime(const Arguments& args) { @@ -3809,17 +3756,19 @@ class DiffieHellman : public ObjectWrap { char* data = new char[dataSize]; BN_bn2bin(diffieHellman->dh->p, reinterpret_cast(data)); - Local outString; + Handle output; - if (args.Length() > 0 && args[0]->IsString()) { - outString = EncodeWithEncoding(args[0], data, dataSize); + if (args[0]->IsNull()) { + output = Buffer::New((char*) data, dataSize)->handle_; + } else if (args.Length() > 0 && args[0]->IsString()) { + output = EncodeWithEncoding(args[0], data, dataSize); } else { - outString = Encode(data, dataSize, BINARY); + output = Encode(data, dataSize, BINARY); } delete[] data; - return scope.Close(outString); + return scope.Close(output); } static Handle GetGenerator(const Arguments& args) { @@ -3836,17 +3785,19 @@ class DiffieHellman : public ObjectWrap { char* data = new char[dataSize]; BN_bn2bin(diffieHellman->dh->g, reinterpret_cast(data)); - Local outString; + Handle output; - if (args.Length() > 0 && args[0]->IsString()) { - outString = EncodeWithEncoding(args[0], data, dataSize); + if (args[0]->IsNull()) { + output = Buffer::New((char*) data, dataSize)->handle_; + } else if (args.Length() > 0 && args[0]->IsString()) { + output = EncodeWithEncoding(args[0], data, dataSize); } else { - outString = Encode(data, dataSize, BINARY); + output = Encode(data, dataSize, BINARY); } delete[] data; - return scope.Close(outString); + return scope.Close(output); } static Handle GetPublicKey(const Arguments& args) { @@ -3869,17 +3820,19 @@ class DiffieHellman : public ObjectWrap { BN_bn2bin(diffieHellman->dh->pub_key, reinterpret_cast(data)); - Local outString; + Handle output; - if (args.Length() > 0 && args[0]->IsString()) { - outString = EncodeWithEncoding(args[0], data, dataSize); + if (args[0]->IsNull()) { + output = Buffer::New((char*) data, dataSize)->handle_; + } else if (args.Length() > 0 && args[0]->IsString()) { + output = EncodeWithEncoding(args[0], data, dataSize); } else { - outString = Encode(data, dataSize, BINARY); + output = Encode(data, dataSize, BINARY); } delete[] data; - return scope.Close(outString); + return scope.Close(output); } static Handle GetPrivateKey(const Arguments& args) { @@ -3902,17 +3855,19 @@ class DiffieHellman : public ObjectWrap { BN_bn2bin(diffieHellman->dh->priv_key, reinterpret_cast(data)); - Local outString; + Handle output; - if (args.Length() > 0 && args[0]->IsString()) { - outString = EncodeWithEncoding(args[0], data, dataSize); + if (args[0]->IsNull()) { + output = Buffer::New((char*) data, dataSize)->handle_; + } else if (args.Length() > 0 && args[0]->IsString()) { + output = EncodeWithEncoding(args[0], data, dataSize); } else { - outString = Encode(data, dataSize, BINARY); + output = Encode(data, dataSize, BINARY); } delete[] data; - return scope.Close(outString); + return scope.Close(output); } static Handle ComputeSecret(const Arguments& args) { @@ -3964,7 +3919,7 @@ class DiffieHellman : public ObjectWrap { key, diffieHellman->dh); BN_free(key); - Local outString; + Handle output; // DH_size returns number of bytes in a prime number // DH_compute_key returns number of bytes in a remainder of exponent, which @@ -3993,17 +3948,19 @@ class DiffieHellman : public ObjectWrap { return ThrowException(Exception::Error(String::New("Invalid key"))); } } else { - if (args.Length() > 2 && args[2]->IsString()) { - outString = EncodeWithEncoding(args[2], data, dataSize); + if (args.Length() > 2 && args[2]->IsNull()) { + output = Buffer::New((char*) data, dataSize)->handle_; + } else if (args.Length() > 2 && args[2]->IsString()) { + output = EncodeWithEncoding(args[2], data, dataSize); } else if (args.Length() > 1 && args[1]->IsString()) { - outString = EncodeWithEncoding(args[1], data, dataSize); + output = EncodeWithEncoding(args[1], data, dataSize); } else { - outString = Encode(data, dataSize, BINARY); + output = Encode(data, dataSize, BINARY); } } delete[] data; - return scope.Close(outString); + return scope.Close(output); } static Handle SetPublicKey(const Arguments& args) { diff --git a/test/simple/test-crypto.js b/test/simple/test-crypto.js index de8c1a9d638..bfa115453a4 100644 --- a/test/simple/test-crypto.js +++ b/test/simple/test-crypto.js @@ -364,6 +364,12 @@ for (var i = 0, l = rfc2202_sha1.length; i < l; i++) { 'Test HMAC-SHA1 : Test case ' + (i + 1) + ' rfc 2202'); } +var db = crypto.createHmac('md5', rfc2202_md5[0]['key']) + .update(rfc2202_md5[0]['data']) + .digest(null); +assert.ok(Buffer.isBuffer(db)); +assert.equal(rfc2202_md5[0]['hmac'], db.toString('hex'), 'hmac digest buffer'); + // Test hashing var a0 = crypto.createHash('sha1').update('Test123').digest('hex'); var a1 = crypto.createHash('md5').update('Test123').digest('binary'); @@ -399,6 +405,9 @@ fileStream.on('close', function() { '22723e553129a336ad96e10f6aecdf0f45e4149e', 'Test SHA1 of sample.png'); }); +var db = crypto.createHash('sha1').update(new Buffer('Test123')).digest(null); +assert.ok(Buffer.isBuffer(db)); +assert.equal(h1, db.toString('hex'), 'hash digest buffer'); // Issue #2227: unknown digest method should throw an error. assert.throws(function() { @@ -436,10 +445,38 @@ function testCipher1(key) { // Only use binary or hex, not base64. ciph += cipher.final('hex'); + cipher = crypto.createCipher('aes192', key); + //cypher text as buffer + var ctb1 = cipher.update(plaintext, 'utf8', null); + var ctb2 = cipher.final(null); + + assert.ok(Buffer.isBuffer(ctb1)); + assert.ok(Buffer.isBuffer(ctb2)); + + var ctb = new Buffer(ctb1.length + ctb2.length); + ctb1.copy(ctb); + ctb2.copy(ctb, ctb1.length); + + assert.equal(ciph, ctb.toString('hex'), 'cipher output as buffer'); + var decipher = crypto.createDecipher('aes192', key); var txt = decipher.update(ciph, 'hex', 'utf8'); txt += decipher.final('utf8'); + decipher = crypto.createDecipher('aes192', 'MySecretKey123'); + //plain text as buffer + var ptb1 = decipher.update(ciph, 'hex', null); + var ptb2 = decipher.final(null); + + assert.ok(Buffer.isBuffer(ptb1)); + assert.ok(Buffer.isBuffer(ptb2)); + + var ptb = new Buffer(ptb1.length + ptb2.length); + ptb1.copy(ptb); + ptb2.copy(ptb, ptb1.length); + + assert.equal(txt, ptb.toString('utf8'), 'decipher output as buffer'); + assert.equal(txt, plaintext, 'encryption and decryption'); } @@ -521,15 +558,43 @@ var privkey1 = dh1.getPrivateKey(); dh3.setPublicKey(key1); dh3.setPrivateKey(privkey1); -assert.equal(dh1.getPrime(), dh3.getPrime()); -assert.equal(dh1.getGenerator(), dh3.getGenerator()); -assert.equal(dh1.getPublicKey(), dh3.getPublicKey()); -assert.equal(dh1.getPrivateKey(), dh3.getPrivateKey()); +assert.equal(dh1.getPrime(null).toString('binary'), dh3.getPrime()); +assert.equal(dh1.getGenerator(), dh3.getGenerator(null).toString('binary')); +assert.equal(dh1.getPublicKey(null).toString('base64'), dh3.getPublicKey(null).toString('base64')); +assert.equal(dh1.getPrivateKey(null).toString('binary'), dh3.getPrivateKey()); +assert.ok(Buffer.isBuffer(dh1.getPrime(null))); +assert.ok(Buffer.isBuffer(dh1.getGenerator(null))); +assert.ok(Buffer.isBuffer(dh1.getPublicKey(null))); +assert.ok(Buffer.isBuffer(dh1.getPrivateKey(null))); var secret3 = dh3.computeSecret(key2, 'hex', 'base64'); assert.equal(secret1, secret3); +// Test with yet another dh1, now using buffers +var dh4 = crypto.createDiffieHellman(new Buffer(p1, 'base64')); +dh4.setPublicKey(key1); +dh4.setPrivateKey(privkey1); + +var secret4 = dh4.computeSecret(new Buffer(key2, 'hex'), null, 'base64'); + +assert.equal(secret1, secret4); + +// Test Diffie-Hellman outputing buffers +var dh5 = crypto.createDiffieHellman(256); +var p2 = dh5.getPrime(null); +var dh6 = crypto.createDiffieHellman(p2, null); +var key5 = dh5.generateKeys(null); +var key6 = dh6.generateKeys('base64'); +var secret5 = dh5.computeSecret(key6, 'base64', null); +var secret6 = dh6.computeSecret(key5, null, null); + +assert.ok(Buffer.isBuffer(key5)); +assert.ok(Buffer.isBuffer(p2)); +assert.ok(Buffer.isBuffer(secret5)); +assert.ok(Buffer.isBuffer(secret6)); +assert.equal(secret5.toString('hex'), secret6.toString('hex')); + // https://github.com/joyent/node/issues/2338 assert.throws(function() { var p = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' + @@ -557,6 +622,16 @@ assert.equal(rsaSignature, rsaVerify.update(rsaPubPem); assert.strictEqual(rsaVerify.verify(rsaPubPem, rsaSignature, 'hex'), true); +var bufSign = crypto.createSign('RSA-SHA1'); +var bufVerify = crypto.createVerify('RSA-SHA1'); + +bufSign.update(rsaPubPem); +var bufSignature = bufSign.sign(rsaKeyPem, null); +assert.ok(Buffer.isBuffer(bufSignature)); +assert.equal(rsaSignature, bufSignature.toString('hex')); + +bufVerify.update(rsaPubPem); +assert.strictEqual(bufVerify.verify(rsaPubPem, bufSignature), true); // // Test RSA signing and verification