diff --git a/doc/api/crypto.markdown b/doc/api/crypto.markdown index 0c0134c39ad..19b88c2f860 100644 --- a/doc/api/crypto.markdown +++ b/doc/api/crypto.markdown @@ -139,11 +139,13 @@ This is the mirror of the signing object above. Updates the verifier object with data. This can be called many times with new data as it is streamed. -### verifier.verify(cert, signature, signature_format='binary') +### verifier.verify(object, signature, signature_format='binary') -Verifies the signed data by using the `cert` which is a string containing -the PEM encoded certificate, and `signature`, which is the previously calculated -signature for the data, in the `signature_format` which can be `'binary'`, `'hex'` or `'base64'`. +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'`. Returns true or false depending on the validity of the signature for the data and public key. diff --git a/doc/api/modules.markdown b/doc/api/modules.markdown index 1bb185e1766..2e943bf2c11 100644 --- a/doc/api/modules.markdown +++ b/doc/api/modules.markdown @@ -330,6 +330,7 @@ Because `module` provides a `filename` property (normally equivalent to `__filename`), the entry point of the current application can be obtained by checking `require.main.filename`. + ## AMD Compatibility Node's modules have access to a function named `define`, which may be @@ -362,6 +363,23 @@ The example module above could be structured like so: Node executes the callback immediately, so please plan your programs accordingly. + +### Accessing the main module + +When a file is run directly from Node, `require.main` is set to its +`module`. That means that you can determine whether a file has been run +directly by testing + + require.main === module + +For a file `foo.js`, this will be `true` if run via `node foo.js`, but +`false` if run by `require('./foo')`. + +Because `module` provides a `filename` property (normally equivalent to +`__filename`), the entry point of the current application can be obtained +by checking `require.main.filename`. + + ## Addenda: Package Manager Tips The semantics of Node's `require()` function were designed to be general diff --git a/lib/util.js b/lib/util.js index 4241ba0f074..02394344578 100644 --- a/lib/util.js +++ b/lib/util.js @@ -423,6 +423,11 @@ exports.pump = function(readStream, writeStream, callback) { exports.inherits = function(ctor, superCtor) { ctor.super_ = superCtor; ctor.prototype = Object.create(superCtor.prototype, { - constructor: { value: ctor, enumerable: false } + constructor: { + value: ctor, + enumerable: false, + writable: true, + configurable: true + } }); }; diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 13749a83fec..20a475e35e5 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -42,6 +42,11 @@ return ThrowException(Exception::TypeError(String::New("Not a string or buffer"))); \ } +static const char *RSA_PUB_KEY_PFX = "-----BEGIN RSA PUBLIC KEY-----"; +static const char *DSA_PUB_KEY_PFX = "-----BEGIN PUBLIC KEY-----"; +static const int RSA_PUB_KEY_PFX_LEN = strlen(RSA_PUB_KEY_PFX); +static const int DSA_PUB_KEY_PFX_LEN = strlen(DSA_PUB_KEY_PFX); + namespace node { namespace crypto { @@ -1647,7 +1652,7 @@ class Cipher : public ObjectWrap { static Handle CipherInitIv(const Arguments& args) { Cipher *cipher = ObjectWrap::Unwrap(args.This()); - + HandleScope scope; cipher->incomplete_base64=NULL; @@ -1682,7 +1687,7 @@ class Cipher : public ObjectWrap { assert(iv_written == iv_len); String::Utf8Value cipherType(args[0]->ToString()); - + bool r = cipher->CipherInitIv(*cipherType, key_buf,key_len,iv_buf,iv_len); delete [] key_buf; @@ -1976,7 +1981,7 @@ class Decipher : public ObjectWrap { static Handle DecipherInit(const Arguments& args) { Decipher *cipher = ObjectWrap::Unwrap(args.This()); - + HandleScope scope; cipher->incomplete_utf8=NULL; @@ -2000,7 +2005,7 @@ class Decipher : public ObjectWrap { assert(key_written == key_len); String::Utf8Value cipherType(args[0]->ToString()); - + bool r = cipher->DecipherInit(*cipherType, key_buf,key_len); delete [] key_buf; @@ -2014,7 +2019,7 @@ class Decipher : public ObjectWrap { static Handle DecipherInitIv(const Arguments& args) { Decipher *cipher = ObjectWrap::Unwrap(args.This()); - + HandleScope scope; cipher->incomplete_utf8=NULL; @@ -2050,7 +2055,7 @@ class Decipher : public ObjectWrap { assert(iv_written == iv_len); String::Utf8Value cipherType(args[0]->ToString()); - + bool r = cipher->DecipherInitIv(*cipherType, key_buf,key_len,iv_buf,iv_len); delete [] key_buf; @@ -2415,7 +2420,7 @@ class Hmac : public ObjectWrap { } int r; - + if( Buffer::HasInstance(args[0])) { Local buffer_obj = args[0]->ToObject(); char *buffer_data = Buffer::Data(buffer_obj); @@ -2906,29 +2911,58 @@ class Verify : public ObjectWrap { int VerifyFinal(char* key_pem, int key_pemLen, unsigned char* sig, int siglen) { if (!initialised_) return 0; + EVP_PKEY* pkey = NULL; BIO *bp = NULL; - EVP_PKEY* pkey; - X509 *x509; + X509 *x509 = NULL; + int r = 0; bp = BIO_new(BIO_s_mem()); - if(!BIO_write(bp, key_pem, key_pemLen)) return 0; - - x509 = PEM_read_bio_X509(bp, NULL, NULL, NULL ); - if (x509==NULL) return 0; - - pkey=X509_get_pubkey(x509); - if (pkey==NULL) return 0; - - int r = EVP_VerifyFinal(&mdctx, sig, siglen, pkey); - EVP_PKEY_free (pkey); - - if (r != 1) { - ERR_print_errors_fp (stderr); + if (bp == NULL) { + ERR_print_errors_fp(stderr); + return 0; } - X509_free(x509); - BIO_free(bp); + if(!BIO_write(bp, key_pem, key_pemLen)) { + ERR_print_errors_fp(stderr); + return 0; + } + + // Check if this is an RSA or DSA "raw" public key before trying + // X.509 + if (strncmp(key_pem, RSA_PUB_KEY_PFX, RSA_PUB_KEY_PFX_LEN) == 0 || + strncmp(key_pem, DSA_PUB_KEY_PFX, DSA_PUB_KEY_PFX_LEN) == 0) { + pkey = PEM_read_bio_PUBKEY(bp, NULL, NULL, NULL); + if (pkey == NULL) { + ERR_print_errors_fp(stderr); + return 0; + } + } else { + // X.509 fallback + x509 = PEM_read_bio_X509(bp, NULL, NULL, NULL); + if (x509 == NULL) { + ERR_print_errors_fp(stderr); + return 0; + } + + pkey = X509_get_pubkey(x509); + if (pkey == NULL) { + ERR_print_errors_fp(stderr); + return 0; + } + } + + r = EVP_VerifyFinal(&mdctx, sig, siglen, pkey); + if (r != 1) + ERR_print_errors_fp (stderr); + + if(pkey != NULL) + EVP_PKEY_free (pkey); + if (x509 != NULL) + X509_free(x509); + if (bp != NULL) + BIO_free(bp); EVP_MD_CTX_cleanup(&mdctx); initialised_ = false; + return r; } diff --git a/test/fixtures/test_rsa_privkey.pem b/test/fixtures/test_rsa_privkey.pem new file mode 100644 index 00000000000..425518a066b --- /dev/null +++ b/test/fixtures/test_rsa_privkey.pem @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXgIBAAKBgQDCFENGw33yGihy92pDjZQhl0C36rPJj+CvfSC8+q28hxA161QF +NUd13wuCTUcq0Qd2qsBe/2hFyc2DCJJg0h1L78+6Z4UMR7EOcpfdUE9Hf3m/hs+F +UR45uBJeDK1HSFHD8bHKD6kv8FPGfJTotc+2xjJwoYi+1hqp1fIekaxsyQIDAQAB +AoGBAJR8ZkCUvx5kzv+utdl7T5MnordT1TvoXXJGXK7ZZ+UuvMNUCdN2QPc4sBiA +QWvLw1cSKt5DsKZ8UETpYPy8pPYnnDEz2dDYiaew9+xEpubyeW2oH4Zx71wqBtOK +kqwrXa/pzdpiucRRjk6vE6YY7EBBs/g7uanVpGibOVAEsqH1AkEA7DkjVH28WDUg +f1nqvfn2Kj6CT7nIcE3jGJsZZ7zlZmBmHFDONMLUrXR/Zm3pR5m0tCmBqa5RK95u +412jt1dPIwJBANJT3v8pnkth48bQo/fKel6uEYyboRtA5/uHuHkZ6FQF7OUkGogc +mSJluOdc5t6hI1VsLn0QZEjQZMEOWr+wKSMCQQCC4kXJEsHAve77oP6HtG/IiEn7 +kpyUXRNvFsDE0czpJJBvL/aRFUJxuRK91jhjC68sA7NsKMGg5OXb5I5Jj36xAkEA +gIT7aFOYBFwGgQAQkWNKLvySgKbAZRTeLBacpHMuQdl1DfdntvAyqpAZ0lY0RKmW +G6aFKaqQfOXKCyWoUiVknQJAXrlgySFci/2ueKlIE1QqIiLSZ8V8OlpFLRnb1pzI +7U1yQXnTAEFYM560yJlzUpOb1V4cScGd365tiSMvxLOvTA== +-----END RSA PRIVATE KEY----- diff --git a/test/fixtures/test_rsa_pubkey.pem b/test/fixtures/test_rsa_pubkey.pem new file mode 100644 index 00000000000..b3bbf6cb906 --- /dev/null +++ b/test/fixtures/test_rsa_pubkey.pem @@ -0,0 +1,6 @@ +-----BEGIN PUBLIC KEY----- +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDCFENGw33yGihy92pDjZQhl0C3 +6rPJj+CvfSC8+q28hxA161QFNUd13wuCTUcq0Qd2qsBe/2hFyc2DCJJg0h1L78+6 +Z4UMR7EOcpfdUE9Hf3m/hs+FUR45uBJeDK1HSFHD8bHKD6kv8FPGfJTotc+2xjJw +oYi+1hqp1fIekaxsyQIDAQAB +-----END PUBLIC KEY----- diff --git a/test/simple/test-crypto.js b/test/simple/test-crypto.js index aefdc233d98..9f2d6f32e0f 100644 --- a/test/simple/test-crypto.js +++ b/test/simple/test-crypto.js @@ -36,6 +36,8 @@ var path = require('path'); var caPem = fs.readFileSync(common.fixturesDir + '/test_ca.pem', 'ascii'); var certPem = fs.readFileSync(common.fixturesDir + '/test_cert.pem', 'ascii'); var keyPem = fs.readFileSync(common.fixturesDir + '/test_key.pem', 'ascii'); +var rsaPubPem = fs.readFileSync(common.fixturesDir + '/test_rsa_pubkey.pem', 'ascii'); +var rsaKeyPem = fs.readFileSync(common.fixturesDir + '/test_rsa_privkey.pem', 'ascii'); try { var credentials = crypto.createCredentials( @@ -145,6 +147,7 @@ assert.throws(function() { crypto.createHash('sha1').update({foo: 'bar'}); }, /string or buffer/); + // Test Diffie-Hellman with two parties sharing a secret, // using various encodings as we go along var dh1 = crypto.createDiffieHellman(256); @@ -172,3 +175,17 @@ assert.equal(dh1.getPrivateKey(), dh3.getPrivateKey()); var secret3 = dh3.computeSecret(key2, 'hex', 'base64'); assert.equal(secret1, secret3); + + +// Test RSA key signing/verification +var rsaSign = crypto.createSign('RSA-SHA1'); +var rsaVerify = crypto.createVerify('RSA-SHA1'); +assert.ok(rsaSign); +assert.ok(rsaVerify); + +rsaSign.update(rsaPubPem); +var rsaSignature = rsaSign.sign(rsaKeyPem, 'hex'); +assert.equal(rsaSignature, '5c50e3145c4e2497aadb0eabc83b342d0b0021ece0d4c4a064b7c8f020d7e2688b122bfb54c724ac9ee169f83f66d2fe90abeb95e8e1290e7e177152a4de3d944cf7d4883114a20ed0f78e70e25ef0f60f06b858e6af42a2f276ede95bbc6bc9a9bbdda15bd663186a6f40819a7af19e577bb2efa5e579a1f5ce8a0d4ca8b8f6'); + +rsaVerify.update(rsaPubPem); +assert.equal(rsaVerify.verify(rsaPubPem, rsaSignature, 'hex'), 1);