├── .gitignore ├── test ├── .eslintrc.json ├── pss_tv.pub ├── blort.sshpub ├── blort.pub ├── zorch.pub ├── another_zorch.pem ├── zorch.pem ├── blort.pem ├── blort-pass.pem ├── fixture.js ├── test.js └── native.js ├── .travis.yml ├── package.json ├── binding.gyp ├── src ├── ursaNative.h └── ursaNative.cc ├── LICENSE.txt ├── README.md └── lib └── ursa.js /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | ursaNative.node 3 | node_modules 4 | npm-debug.log 5 | -------------------------------------------------------------------------------- /test/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-unused-vars": [2] 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /test/pss_tv.pub: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCiukDuB+OyvS8CziJ/NqGVAkSG 3 | 5JwZy0G7vfu6mLIrDld8Lur/og2IOnbmXjlMadSzwFoej63aJ+2ypCvAAP6Ii5sy 4 | wi0VrdDNdrPnk24ZlVsiDdF9TqkEsewQKy5N53USIqqZFRAkx8tBzF6iHQDutB98 5 | gAg00sbga847zn6ppQIDAQAB 6 | -----END PUBLIC KEY----- 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | sudo: false 3 | env: 4 | - CXX=g++-4.8 5 | node_js: 6 | - "0.12" 7 | - "4" 8 | - "5" 9 | - "6" 10 | - "7" 11 | - "8" 12 | - "9" 13 | - "10" 14 | addons: 15 | apt: 16 | sources: 17 | - ubuntu-toolchain-r-test 18 | packages: 19 | - g++-4.8 20 | -------------------------------------------------------------------------------- /test/blort.sshpub: -------------------------------------------------------------------------------- 1 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCuCi/QodViU61LXn9Yg7QenP00i5ZCIf/1W4KqMSewwl1lHbQ2zGI0KMxOM5e2XyMIYUCnx/Jvml5PtCWnjFmTLqh17IURzpn1AifJFJUGjOVGhhKF59DolI8VoX2T4VgU3/HPQtgdnBkJb6vvx1BX1kYoFBPrUSXw2KuKKoqrVIFmIQjuNPXwnSLofWoVUCSRlzLPe/zn/K509QLXAEXDN+T3In07xuk2UaifGUOhApfEdLzJXXl1OoACh5XKwGpCTX82INC4Qkxuurdx8OWXSxyydV5zR3AhQ1j1RqzGts+nCTTRx7ni5aPBiX+xD4A68pmEldskUR8rIWLx/YR1 2 | -------------------------------------------------------------------------------- /test/blort.pub: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArgov0KHVYlOtS15/WIO0 3 | Hpz9NIuWQiH/9VuCqjEnsMJdZR20NsxiNCjMTjOXtl8jCGFAp8fyb5peT7Qlp4xZ 4 | ky6odeyFEc6Z9QInyRSVBozlRoYShefQ6JSPFaF9k+FYFN/xz0LYHZwZCW+r78dQ 5 | V9ZGKBQT61El8NiriiqKq1SBZiEI7jT18J0i6H1qFVAkkZcyz3v85/yudPUC1wBF 6 | wzfk9yJ9O8bpNlGonxlDoQKXxHS8yV15dTqAAoeVysBqQk1/NiDQuEJMbrq3cfDl 7 | l0scsnVec0dwIUNY9UasxrbPpwk00ce54uWjwYl/sQ+AOvKZhJXbJFEfKyFi8f2E 8 | dQIDAQAB 9 | -----END PUBLIC KEY----- 10 | -------------------------------------------------------------------------------- /test/zorch.pub: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtZPKWwG02rHQAZP95ckE 3 | o/rY7nAOJO+TRT5Kf7Ym4xJGBMCc5Ach5j4o1OFzKWaHgtfGM01Le3fdGx4VwxLc 4 | SlUI9ixvlhhKEqkRpGr8vphCDx0vg+jXseQ2axrR6uHrdYA+8ASnfiuxILDXCRLw 5 | mPlliFA4D0qoWenOmqeNT7+98opZeN0BQxiOE/MXluSVpdVK/IQo5ggFDMBrHmq2 6 | +5L2I7JNkZOxADcilecrSrg1bt3FppKWGt49tpcCldOFx50iXE7ca1y4fOtBx0rm 7 | 4JPzmFIYB1hu3mIT/xd3p2MY5dMrQ9kYbeRcxcal1xXreLyWf2KslrPwwxZFR9QV 8 | oQIDAQAB 9 | -----END PUBLIC KEY----- 10 | -------------------------------------------------------------------------------- /test/another_zorch.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIICXAIBAAKBgQDhVzXvfTvYzne0gkD9FHlTYhsuB6wBUIZRNTZaai4EpKzleZbr 3 | hq/R7M4DMA4UbiiuI68fMagcFYgUgfspDb3QBYVSRDpF0OmxGm4ww0LOjsHH/0ZQ 4 | LnsFvbHNQcgJiYkLHQFRi9ARweuZNCRnw3fMY1GhfO5sq7J7SzAAbKQ3vwIDAQAB 5 | AoGAOPTnMBpyZxGA1kJaFN348KeKgS71PfvRh3Mwwte1u/y3quT5zZxkkmYNiGa8 6 | GFPjumhQmkzd1gNnOu/DwRO1Fwbp0bfqwCLnd2Lz400ZrwN/S+hlzu+YR1rKOH7C 7 | 7q/BeAm/hGVejsmkl16WgEpkqTzo11566Gat9UWDAw7C1ZkCQQD2MkPuQH6P5hwF 8 | sy0mr4zfA3+X0L032jyjajakgM9Zn2XnPeYL/R/Cx0h1oBke3CPrKNaRhRCrBwUz 9 | MUx3gkm7AkEA6lBXfCfsiPWzLe6klV+vFaDl40x53AMy+pL0VE/f8GSqHwGPL1q1 10 | aa2AtiSCD505g6vq934V3K/KDK2THug3zQJAHnGIxOVkwRaLUIkfhDEhElK0bGl7 11 | fHSYGvz/VMg427RCPZ4B3Gmoi8VoyGLLuG0wY9vg7I2vyfZMRlBKTFzoEQJAIeh2 12 | gJUWect0npUGZEdwguTB397VU61y1yglC35zncozhEEpg2TRE/XzxmgKGlBaXl+m 13 | pSIt773Qs3z66WIZkQJBAN4pwewrHawHa9yLH5X9zomL8yk7kboRguk2BaZND+KP 14 | ENbnMowAligtYJ7w2KqpHZMJHwIOg8Yd2T9bPZKMW78= 15 | -----END RSA PRIVATE KEY----- 16 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ursa", 3 | "version": "0.9.4", 4 | "keywords": [ 5 | "crypto", 6 | "key", 7 | "openssl", 8 | "private", 9 | "public", 10 | "rsa", 11 | "sign", 12 | "signature", 13 | "verify", 14 | "verification", 15 | "hash", 16 | "digest" 17 | ], 18 | "description": "RSA public/private key OpenSSL bindings for node and io.js", 19 | "homepage": "https://github.com/quartzjer/ursa", 20 | "repository": { 21 | "type": "git", 22 | "url": "https://github.com/quartzjer/ursa.git" 23 | }, 24 | "license": "Apache-2.0", 25 | "author": { 26 | "name": "Dan Bornstein", 27 | "email": "danfuzz@milk.com", 28 | "url": "http://www.milk.com/" 29 | }, 30 | "maintainers": [ 31 | { 32 | "name": "Jeremie Miller", 33 | "email": "jeremie@jabber.org", 34 | "url": "http://jeremie.com/" 35 | } 36 | ], 37 | "main": "lib/ursa.js", 38 | "engines": { 39 | "node": ">=0.10.0" 40 | }, 41 | "scripts": { 42 | "test": "mocha --recursive --reporter spec", 43 | "test-watch": "npm test -- -w --reporter min" 44 | }, 45 | "dependencies": { 46 | "bindings": "^1.2.1", 47 | "nan": "^2.3.3" 48 | }, 49 | "devDependencies": { 50 | "mocha": "^3.2.0" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | 'target_name': 'ursaNative', 5 | 'sources': [ 'src/ursaNative.cc' ], 6 | 'conditions': [ 7 | [ 'OS=="win"', { 8 | 'conditions': [ 9 | # "openssl_root" is the directory on Windows of the OpenSSL files 10 | ['target_arch=="x64"', { 11 | 'variables': { 12 | 'openssl_root%': 'C:/OpenSSL-Win64' 13 | }, 14 | }, { 15 | 'variables': { 16 | 'openssl_root%': 'C:/OpenSSL-Win32' 17 | }, 18 | }], 19 | ], 20 | 'defines': [ 21 | 'uint=unsigned int', 22 | ], 23 | 'libraries': [ 24 | '-l<(openssl_root)/lib/libeay32.lib', 25 | ], 26 | 'include_dirs': [ 27 | '<(openssl_root)/include', 28 | " 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | class RsaWrap : public node::ObjectWrap 16 | { 17 | public: 18 | static void InitClass(v8::Local target); 19 | 20 | protected: 21 | RsaWrap(); 22 | ~RsaWrap(); 23 | 24 | static NAN_METHOD(New); 25 | static NAN_METHOD(GeneratePrivateKey); 26 | static NAN_METHOD(GetExponent); 27 | static NAN_METHOD(GetPrivateExponent); 28 | static NAN_METHOD(GetModulus); 29 | static NAN_METHOD(GetPrivateKeyPem); 30 | static NAN_METHOD(GetPublicKeyPem); 31 | static NAN_METHOD(PrivateDecrypt); 32 | static NAN_METHOD(PrivateEncrypt); 33 | static NAN_METHOD(PublicDecrypt); 34 | static NAN_METHOD(PublicEncrypt); 35 | static NAN_METHOD(SetPrivateKeyPem); 36 | static NAN_METHOD(SetPublicKeyPem); 37 | static NAN_METHOD(Sign); 38 | static NAN_METHOD(Verify); 39 | static NAN_METHOD(CreatePrivateKeyFromComponents); 40 | static NAN_METHOD(CreatePublicKeyFromComponents); 41 | static NAN_METHOD(OpenPublicSshKey); 42 | static NAN_METHOD(AddPSSPadding); 43 | static NAN_METHOD(VerifyPSSPadding); 44 | 45 | private: 46 | static RsaWrap *expectPrivateKey(RsaWrap *obj); 47 | static RsaWrap *expectSet(RsaWrap *obj); 48 | static RsaWrap *expectUnset(RsaWrap *obj); 49 | 50 | BIGNUM *rsa_n, *rsa_e, *rsa_d; 51 | RSA *rsa; 52 | }; 53 | 54 | NAN_METHOD(TextToNid); 55 | 56 | #endif // def URSA_NATIVE_H 57 | -------------------------------------------------------------------------------- /test/zorch.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEogIBAAKCAQEAtZPKWwG02rHQAZP95ckEo/rY7nAOJO+TRT5Kf7Ym4xJGBMCc 3 | 5Ach5j4o1OFzKWaHgtfGM01Le3fdGx4VwxLcSlUI9ixvlhhKEqkRpGr8vphCDx0v 4 | g+jXseQ2axrR6uHrdYA+8ASnfiuxILDXCRLwmPlliFA4D0qoWenOmqeNT7+98opZ 5 | eN0BQxiOE/MXluSVpdVK/IQo5ggFDMBrHmq2+5L2I7JNkZOxADcilecrSrg1bt3F 6 | ppKWGt49tpcCldOFx50iXE7ca1y4fOtBx0rm4JPzmFIYB1hu3mIT/xd3p2MY5dMr 7 | Q9kYbeRcxcal1xXreLyWf2KslrPwwxZFR9QVoQIDAQABAoIBAGhaJ1FmCaolxoUh 8 | qCkG/cO/xixB+d8AUILa6bW72V1mgxb4GzJxZuoLjyvI5YZFhluL5jxVj6vFlyye 9 | faM+k5ukgyH3J6n7C5bt01XKprZiipRlEYmdp1h071FeeIWkkM1WhtUp15iLQ6Cm 10 | AO8WE2/W5KMSdyVSoq4J0NLQuEW705nRBw0KvKOu/16MI3ASIYAhDh2N+celiq0B 11 | QEFdMNIY4mQwRmoW8+nTVh3szVd6GXMH0etcQlDz7dLwXYm4qSw+dQusFjYZ6s4U 12 | HKjIxFsCy32B8XoLKi2gKY0u4SE56H9Qcp05y1fZe20lfenf3Der0ucrX0THj1/Z 13 | HLSTJgECgYEA6qlVbK0gm/iX8p62U/t3e1NtHyv3UlmmFPuHS4Z/cX4hErym0FJk 14 | ln/Ggo2cSlyw83TjQFPRGvCDbULa/pKJow0PyGk0+LHELk5bguGtq0EMpd2ZmUKs 15 | QIPcWXzbzismuH3x8zcNvErCEWvU7aDW8NznZes/a4aIjUR8DdG/l6UCgYEAxha2 16 | /EBp5YTubNIIVSROUMpzYmrJJSMdvCz7eu0yfREQFzAbgyhdOkBx7jdAzAVAINOw 17 | iVNP2h/DcOwGgcTun7RpBQ4QToDviNKQwp2Vh/DTYpuGzYgDJzmb+UawXQhXR9/F 18 | eWzYkb2HFZVQ6Quhd0uFE71B4NFD1FPqEDfERU0CgYBBPt/fnauJcm9dKD/tzeeE 19 | xMd8eU8E+KQzBVSy7SyWM8miWg3PsnkBV3msZw9jpa4VoxRkmGl1ohYI1SPq0Hew 20 | fDs9L/NoipTPgz3ygCk3ipinrZu9f0pBjehAgXTkOB9GAM67Hz2UcvzOtzq275eT 21 | 1PgJ4IT7sqZZEQelGAK7eQKBgDunK4Pbggh0d7idp5S8UjlSPl0s+1YLtTCt+y2R 22 | sNqpAMd63U4qIakhLy4lxYWrLxyzoz43sJxnZFvlODBsNdAybE3xZke93GS/xIhX 23 | HGjLxVy0qWvLwedWGfp/pyzdDiu+36Epfi6lfMCrLqp/rihWvcSsAeLKIjsW6i3o 24 | HVshAoGActqcGaI+rt5A1FFwzNbZYiu8QLjtMV+Je8oryP58HZTfDf31vkIi3MdR 25 | G4jDp9POKxkn+AIfX6GOt2skC+mJGACbjPXCcuSaIYS6LyUmdZ22N+063yiiex2M 26 | irL6oqJcmS8k8DD34fhNPC+Z8KMkcqp0xNm9ikmJLNmEY5eeaiI= 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /test/blort.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpAIBAAKCAQEArgov0KHVYlOtS15/WIO0Hpz9NIuWQiH/9VuCqjEnsMJdZR20 3 | NsxiNCjMTjOXtl8jCGFAp8fyb5peT7Qlp4xZky6odeyFEc6Z9QInyRSVBozlRoYS 4 | hefQ6JSPFaF9k+FYFN/xz0LYHZwZCW+r78dQV9ZGKBQT61El8NiriiqKq1SBZiEI 5 | 7jT18J0i6H1qFVAkkZcyz3v85/yudPUC1wBFwzfk9yJ9O8bpNlGonxlDoQKXxHS8 6 | yV15dTqAAoeVysBqQk1/NiDQuEJMbrq3cfDll0scsnVec0dwIUNY9UasxrbPpwk0 7 | 0ce54uWjwYl/sQ+AOvKZhJXbJFEfKyFi8f2EdQIDAQABAoIBADZx7V9hITXvY2kO 8 | mNwB55kkF3oWqNzrcf05sXi4g+giBcGaN0RlT9ttX+ye6zkcLgNzGRzjyg3rxtE8 9 | VANEPslycTTi1wYpEbS6BeQ70hsIfLlBPA4z5QC9aL2llXlrp3hwTl8+VAl/Sb3P 10 | 94O9YH76A1yKW2jkK/Bad99zS4opEvKRaBdMN3epphrUaUbseCckrgQp5/JVxSLm 11 | 0/rQ7fFhGjiTbn7YdMHLzDkESqUqVdpuU7XQYupb3xiLlHMthgNRzhkrPEHOlcyy 12 | l5WIxP5mEoBbIc1WteiwgYkPhHpGFSXkXmsnmuqpI504JXvWyOZ0CVLBpcg1+YOl 13 | 9JG9askCgYEA5sSIz8Z8AJ2uQ4sWfP44kfd2kSFPcEY8ehyRewR+qOtA1S9Jni0y 14 | 5BSYUP3a6Z8eolj70SAVY68WQ1HHVyzPv84RRKYRUiMQPccUZbTjLYtX+fGjriQc 15 | EhchV0ZHSweCsu5S0LdZtcWIF2t4Kq+6fLD8aFjfv5KgzkP5/EhoDNsCgYEAwRHF 16 | CrijUmzqjhielwp72frwJOdjdsb9RT5kZMOvlCyPpxjdTZwW3AXScKrzotYAFzlS 17 | 70FxIcB2ewStZwObMVk91ZWDU8QI4aMf6P/aN7hNILmbPsZ8HXEEYrWzxNFtTtfP 18 | YqLCDMbuvSEaQ3/K+lVU3j/6OlAWVbo72fFBzO8CgYEAuIC+GdFJ53wqjhowWrMv 19 | K5U9GQAW8V9WjxM0RbTsR8X/yfLK20qr+6tztJ2iX1M3PPoEK8mJEHA2TpCd3lcX 20 | qvTo3YghJRifraTz6/VaBVn5dv75rQt13gjcQVkROy3AE6t0Tyeo9CoAiykDpK1n 21 | XrZquEsGabakFwZGYqzJdC0CgYBv4zhpyi78KZqU1rexmKXF4aMh/+aogxFr+4h6 22 | zN0H5fsqP0KhI4Ar6kPWf3vKNnK/Ar2JYkeJ49vF1yQWuwRJSQqIqKj+9nCfMsXf 23 | Zca02932xRmu+6AZ/9Em015nBWdvdan944zJks/wUyrBS2H6SVFxq2n8OUV4UQE2 24 | gJY9iQKBgQDazbQaXKDuJDHTMsvZfIXdhXC0vbWb5P0TJ0lUz5lN5dfLxVj/MyCS 25 | 7Es98RTf+4wcBMBUDO5Qs5/2Wv0ZSkb2SCJ73Tvs7cnJZwY7MpY53wvhr4jIjAog 26 | DdvScUn3F/qn8sURn0uu9Kz9fqrC/tZeANU6cpBBtWykTLb/spmwuQ== 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /test/blort-pass.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | Proc-Type: 4,ENCRYPTED 3 | DEK-Info: DES-EDE3-CBC,0EE0329ADBEED6A1 4 | 5 | CdxXjx5dnHDVa4swmD2X/Q9jsvN8Xcy8rUW7pcn7ijdn4uEpzVax9YD2Ewy9RsuS 6 | 3Qmhon6YdkjzqjShSXa4p2HXziZlZmgqxOHwUn1IxBK/Bne4WgpBWhkVmccCUaYm 7 | rQhx7SwiqghzXXWvs/lDFjV9LdMFT1McUF7YY2jMmLgZjIsGKX0B/wBMF3E0/Zdo 8 | 9rYwelwS+ZYrQFcXxhZI8lg/KMmhgqEgRwjp+9z0j5H3e82Rs7eH68f2SFKld1ZY 9 | 3kQzHtTU8pGwdhWTSgxHmNMBWk2UV+TXhl88yZfYd0ugPZGk1luXu++MQLCktgfS 10 | 9xVpCmZyuxJgY8Trvf/Sc5fSfF/oB7/5TluTH5ap4jn1mELYnc/bPwZ79IKROlUs 11 | LqcZYTRX46NLhNgqQlFyHCqOomxncxkdnkKE5jJ9AFheda4ZUX6ZL9KpCl4J13NG 12 | aEr/D/8e/c7J5GZRWXu6kskWfDHDT/85Shpk32jxxN1/uugc3ENERojWLVoKggT8 13 | gtbOQ2hc3g5EqFkOcofrn74M4fEd5UymJVbr6rcaFoBtwMoTB38u4ILmmdEqYPhk 14 | 1YnJUSy9etN/fJhpS1sye0ic8xrY6ymsQKc2kYvKjCf34zKUNeLHE15f4++DN2nK 15 | 7ECbfBsIZcCJ/EyS+RfO2iaPlgx/J3I3IiQRlZv6zN72ZW8xDeEZUMDzTr8LO/GG 16 | inIWEeXiq3H6Fi7lg1QkZqG5wfsMFWiaBkm5JDCtA7RyAecwaUvetXhMhycBWIrp 17 | /BlyYV1pmGREWzJbN2DHVO8eZJKKEWqp2tCPko3U35+t6ps6OZbiuq4xL9mOaRmx 18 | ukn8kjl1TgoQ1NBCjXEs0q01tlzMf2GoK/KQwqZmIpEJXo0eadyLok7nK11EuDGx 19 | AQmHP8ycXJRC34rbN8HBVE2iMiSkhZGLvXpeEo7wrSM/AflkSWwAT61unpKC26EH 20 | eClranXixvOF8c1peAIjn0eagW4rVTFfZbNDAbKwSeNQY/keSHvmOtq9AHDU8NhN 21 | uFl5o958qkKFyp/bgHw9CfHdvtXDebMfbSekspKcYXmI46sWy73r5AmxVY1Axh4C 22 | lqNm3sKPhQZ2LsX17yM7M1QyBjgytH8/a5ecpFyrAmiAx1BL4mU5d3KAzxCO0sQM 23 | ZVyin3hbTBHffh5kGU5OH2xjquKqHmUZLlNhWIsfYBKdqenXBsQoGPPwL1kVNNxj 24 | uP4AB0Xk/BUIVvKsNfcCTr0iP8YtVXYW4jD0Y+OtoLHc6e4vLUW15cYJZIYfg9zf 25 | 5rwY8MaP2XTcJovNIsKoyEYxfnMmrlTIbaT0H7JE4nbWx4nnR7X6ybk7KbuS4dVJ 26 | uNswQWQr6WJ29kvv4pvNCKAEPXyTQeIWw9bG8ETB7xuDXgzHJkXZPhs4vGOqTxpf 27 | JkeJtR61rnoqFBEhncHMdF9c5fTSZRcYtTt6miNc6CYJs5tdQvY82XN4H/adqghI 28 | NN/bqFAxLxA4RoXmibIybt+IEf2ejCl2xfWPs9ejnctYL+5JLFT4+IpOQDh/3rtG 29 | fSKhaigLCt7yprOsAxxGg/OF6I6gwNgfeujApoTQIgq0X/frF2YvjP7xNLw8oNeU 30 | -----END RSA PRIVATE KEY----- 31 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2012 The Obvious Corporation. 2 | http://obvious.com/ 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | 17 | ------------------------------------------------------------------------- 18 | Apache License 19 | Version 2.0, January 2004 20 | http://www.apache.org/licenses/ 21 | 22 | 23 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 24 | 25 | 1. Definitions. 26 | 27 | "License" shall mean the terms and conditions for use, reproduction, 28 | and distribution as defined by Sections 1 through 9 of this document. 29 | 30 | "Licensor" shall mean the copyright owner or entity authorized by 31 | the copyright owner that is granting the License. 32 | 33 | "Legal Entity" shall mean the union of the acting entity and all 34 | other entities that control, are controlled by, or are under common 35 | control with that entity. For the purposes of this definition, 36 | "control" means (i) the power, direct or indirect, to cause the 37 | direction or management of such entity, whether by contract or 38 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 39 | outstanding shares, or (iii) beneficial ownership of such entity. 40 | 41 | "You" (or "Your") shall mean an individual or Legal Entity 42 | exercising permissions granted by this License. 43 | 44 | "Source" form shall mean the preferred form for making modifications, 45 | including but not limited to software source code, documentation 46 | source, and configuration files. 47 | 48 | "Object" form shall mean any form resulting from mechanical 49 | transformation or translation of a Source form, including but 50 | not limited to compiled object code, generated documentation, 51 | and conversions to other media types. 52 | 53 | "Work" shall mean the work of authorship, whether in Source or 54 | Object form, made available under the License, as indicated by a 55 | copyright notice that is included in or attached to the work 56 | (an example is provided in the Appendix below). 57 | 58 | "Derivative Works" shall mean any work, whether in Source or Object 59 | form, that is based on (or derived from) the Work and for which the 60 | editorial revisions, annotations, elaborations, or other modifications 61 | represent, as a whole, an original work of authorship. For the purposes 62 | of this License, Derivative Works shall not include works that remain 63 | separable from, or merely link (or bind by name) to the interfaces of, 64 | the Work and Derivative Works thereof. 65 | 66 | "Contribution" shall mean any work of authorship, including 67 | the original version of the Work and any modifications or additions 68 | to that Work or Derivative Works thereof, that is intentionally 69 | submitted to Licensor for inclusion in the Work by the copyright owner 70 | or by an individual or Legal Entity authorized to submit on behalf of 71 | the copyright owner. For the purposes of this definition, "submitted" 72 | means any form of electronic, verbal, or written communication sent 73 | to the Licensor or its representatives, including but not limited to 74 | communication on electronic mailing lists, source code control systems, 75 | and issue tracking systems that are managed by, or on behalf of, the 76 | Licensor for the purpose of discussing and improving the Work, but 77 | excluding communication that is conspicuously marked or otherwise 78 | designated in writing by the copyright owner as "Not a Contribution." 79 | 80 | "Contributor" shall mean Licensor and any individual or Legal Entity 81 | on behalf of whom a Contribution has been received by Licensor and 82 | subsequently incorporated within the Work. 83 | 84 | 2. Grant of Copyright License. Subject to the terms and conditions of 85 | this License, each Contributor hereby grants to You a perpetual, 86 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 87 | copyright license to reproduce, prepare Derivative Works of, 88 | publicly display, publicly perform, sublicense, and distribute the 89 | Work and such Derivative Works in Source or Object form. 90 | 91 | 3. Grant of Patent License. Subject to the terms and conditions of 92 | this License, each Contributor hereby grants to You a perpetual, 93 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 94 | (except as stated in this section) patent license to make, have made, 95 | use, offer to sell, sell, import, and otherwise transfer the Work, 96 | where such license applies only to those patent claims licensable 97 | by such Contributor that are necessarily infringed by their 98 | Contribution(s) alone or by combination of their Contribution(s) 99 | with the Work to which such Contribution(s) was submitted. If You 100 | institute patent litigation against any entity (including a 101 | cross-claim or counterclaim in a lawsuit) alleging that the Work 102 | or a Contribution incorporated within the Work constitutes direct 103 | or contributory patent infringement, then any patent licenses 104 | granted to You under this License for that Work shall terminate 105 | as of the date such litigation is filed. 106 | 107 | 4. Redistribution. You may reproduce and distribute copies of the 108 | Work or Derivative Works thereof in any medium, with or without 109 | modifications, and in Source or Object form, provided that You 110 | meet the following conditions: 111 | 112 | (a) You must give any other recipients of the Work or 113 | Derivative Works a copy of this License; and 114 | 115 | (b) You must cause any modified files to carry prominent notices 116 | stating that You changed the files; and 117 | 118 | (c) You must retain, in the Source form of any Derivative Works 119 | that You distribute, all copyright, patent, trademark, and 120 | attribution notices from the Source form of the Work, 121 | excluding those notices that do not pertain to any part of 122 | the Derivative Works; and 123 | 124 | (d) If the Work includes a "NOTICE" text file as part of its 125 | distribution, then any Derivative Works that You distribute must 126 | include a readable copy of the attribution notices contained 127 | within such NOTICE file, excluding those notices that do not 128 | pertain to any part of the Derivative Works, in at least one 129 | of the following places: within a NOTICE text file distributed 130 | as part of the Derivative Works; within the Source form or 131 | documentation, if provided along with the Derivative Works; or, 132 | within a display generated by the Derivative Works, if and 133 | wherever such third-party notices normally appear. The contents 134 | of the NOTICE file are for informational purposes only and 135 | do not modify the License. You may add Your own attribution 136 | notices within Derivative Works that You distribute, alongside 137 | or as an addendum to the NOTICE text from the Work, provided 138 | that such additional attribution notices cannot be construed 139 | as modifying the License. 140 | 141 | You may add Your own copyright statement to Your modifications and 142 | may provide additional or different license terms and conditions 143 | for use, reproduction, or distribution of Your modifications, or 144 | for any such Derivative Works as a whole, provided Your use, 145 | reproduction, and distribution of the Work otherwise complies with 146 | the conditions stated in this License. 147 | 148 | 5. Submission of Contributions. Unless You explicitly state otherwise, 149 | any Contribution intentionally submitted for inclusion in the Work 150 | by You to the Licensor shall be under the terms and conditions of 151 | this License, without any additional terms or conditions. 152 | Notwithstanding the above, nothing herein shall supersede or modify 153 | the terms of any separate license agreement you may have executed 154 | with Licensor regarding such Contributions. 155 | 156 | 6. Trademarks. This License does not grant permission to use the trade 157 | names, trademarks, service marks, or product names of the Licensor, 158 | except as required for reasonable and customary use in describing the 159 | origin of the Work and reproducing the content of the NOTICE file. 160 | 161 | 7. Disclaimer of Warranty. Unless required by applicable law or 162 | agreed to in writing, Licensor provides the Work (and each 163 | Contributor provides its Contributions) on an "AS IS" BASIS, 164 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 165 | implied, including, without limitation, any warranties or conditions 166 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 167 | PARTICULAR PURPOSE. You are solely responsible for determining the 168 | appropriateness of using or redistributing the Work and assume any 169 | risks associated with Your exercise of permissions under this License. 170 | 171 | 8. Limitation of Liability. In no event and under no legal theory, 172 | whether in tort (including negligence), contract, or otherwise, 173 | unless required by applicable law (such as deliberate and grossly 174 | negligent acts) or agreed to in writing, shall any Contributor be 175 | liable to You for damages, including any direct, indirect, special, 176 | incidental, or consequential damages of any character arising as a 177 | result of this License or out of the use or inability to use the 178 | Work (including but not limited to damages for loss of goodwill, 179 | work stoppage, computer failure or malfunction, or any and all 180 | other commercial damages or losses), even if such Contributor 181 | has been advised of the possibility of such damages. 182 | 183 | 9. Accepting Warranty or Additional Liability. While redistributing 184 | the Work or Derivative Works thereof, You may choose to offer, 185 | and charge a fee for, acceptance of support, warranty, indemnity, 186 | or other liability obligations and/or rights consistent with this 187 | License. However, in accepting such obligations, You may act only 188 | on Your own behalf and on Your sole responsibility, not on behalf 189 | of any other Contributor, and only if You agree to indemnify, 190 | defend, and hold each Contributor harmless for any liability 191 | incurred by, or claims asserted against, such Contributor by reason 192 | of your accepting any such warranty or additional liability. 193 | 194 | END OF TERMS AND CONDITIONS 195 | -------------------------------------------------------------------------------- /test/fixture.js: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Obvious Corporation. 2 | 3 | /* 4 | * Common fixture for use across tests 5 | */ 6 | 7 | /* 8 | * Modules used 9 | */ 10 | 11 | "use strict"; 12 | 13 | var fs = require("fs"); 14 | 15 | var ursa = require("../lib/ursa"); 16 | var ursaNative = require("bindings")("ursaNative"); 17 | 18 | 19 | /* 20 | * Variable definitions 21 | */ 22 | 23 | var BASE64 = "base64"; 24 | var BINARY = "binary"; 25 | var HEX = "hex"; 26 | var SHA1 = "sha1"; 27 | var SHA256 = "sha256"; 28 | var UTF8 = "utf8"; 29 | var DES_EDE3_CBC = "des-ede3-cbc"; 30 | 31 | var PASS_PRIVATE_KEY = fs.readFileSync(__dirname + "/blort-pass.pem"); 32 | var PRIVATE_KEY = fs.readFileSync(__dirname + "/blort.pem"); 33 | var PUBLIC_KEY = fs.readFileSync(__dirname + "/blort.pub"); 34 | var SSH_PUBLIC_KEY_FILE = fs.readFileSync(__dirname + "/blort.sshpub"); 35 | var PRIVATE_KEY_2 = fs.readFileSync(__dirname + "/zorch.pem"); 36 | var PUBLIC_KEY_2 = fs.readFileSync(__dirname + "/zorch.pub"); 37 | var PRIVATE_KEY_2 = fs.readFileSync(__dirname + "/zorch.pem"); 38 | var PRIVATE_KEY_3 = fs.readFileSync(__dirname + "/another_zorch.pem"); 39 | 40 | var PASSWORD = new Buffer("biscuits", UTF8); 41 | 42 | var EXPONENT_HEX = "010001"; 43 | var MODULUS_HEX = 44 | "ae0a2fd0a1d56253ad4b5e7f5883b41e9cfd348b964221fff55b82aa3127b0c2" + 45 | "5d651db436cc623428cc4e3397b65f23086140a7c7f26f9a5e4fb425a78c5993" + 46 | "2ea875ec8511ce99f50227c91495068ce546861285e7d0e8948f15a17d93e158" + 47 | "14dff1cf42d81d9c19096fabefc75057d646281413eb5125f0d8ab8a2a8aab54" + 48 | "81662108ee34f5f09d22e87d6a155024919732cf7bfce7fcae74f502d70045c3" + 49 | "37e4f7227d3bc6e93651a89f1943a10297c474bcc95d79753a80028795cac06a" + 50 | "424d7f3620d0b8424c6ebab771f0e5974b1cb2755e734770214358f546acc6b6" + 51 | "cfa70934d1c7b9e2e5a3c1897fb10f803af2998495db24511f2b2162f1fd8475"; 52 | 53 | var PLAINTEXT = "Muffins are tasty."; 54 | var PLAINTEXT_PADDED = 55 | "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + 56 | "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + 57 | "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + 58 | "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + 59 | "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + 60 | "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + 61 | "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + 62 | "\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "Muffins are tasty."; 63 | var PRIVATE_CIPHERTEXT_HEX = 64 | "98a96084dc8dfad2c4e604dc20def71acbf784b8b34ecafeb2840e238ac8031c" + 65 | "7559004fa8337d20889b8a582af4f7d3707ab41d0a81487f0d80fb82be49537c" + 66 | "2b9cd8dbb3b772fe0306ff9b4b99faa7cc26d5c04b1e8e79505bac1e8f2cdad2" + 67 | "d3d8680eee3c16db8742b61935fca9679070d278f988ce4d414ab49a544c9088" + 68 | "17a0d340a41384f4b8d826e41031ddcd3f72c29dec2fee0355a8203ea0d381a1" + 69 | "a0f0969804d4968fb2e6220db5cf02e2c2200ff9d0a5a5037ac859a55c005ecc" + 70 | "52ce194a6a9624c71547c96cf90d911caa4097f9cdfded71d23c9f8f5551188c" + 71 | "8326357d54224ab25b9f29c1efdbc960a0968e4c9027cd507ffadd8dff93256c"; 72 | var PRIVATE_OLD_PAD_CIPHER_HEX = 73 | "69d1c385929fc00f89aa98ae9cd8529afe884b581505acdcd4ceaa10bfda9adc" + 74 | "79c472dd7e35bcc94f1146459c6a8d96e572116c7a62f1da5dd18cdb8f81e72b" + 75 | "4a4649f40470e88c11b04fdf72e48c6adb44c41edc0c4c56074a041c03017f72" + 76 | "f66a000066a4dbe888119c83f79e7cb8f667f0af1af41cf4adf21320fada9355" + 77 | "6d056a2fdb1f5a9f5708e096a7408a115efa14f0e2f94feaa32322aa4af9c97a" + 78 | "438d205f62317020e657c5057227a3d7e60a6a6658781cf41b0820988a4f9e8e" + 79 | "b947c424248d231c3e43c711b0c4a4342a0fa484d0e3ded231a695250f4dafcf" + 80 | "f9e94d02e3f74d4c509cfae24b8615e619805c9cdc9e85faed7d706dd6891383"; 81 | var PUBLIC_CIPHERTEXT_HEX = 82 | "16b5e95a02db09e95bb5419998b3c5f450571578be271602828740242236e6aa" + 83 | "0bce325d6b9a681038c864e0877a3e68e20329a3602829128385f182a20f06c7" + 84 | "6f4c82f4f58481ff19ac2db9fd2b6b097047f741fa81a6c6a50b33259f3458b7" + 85 | "5adcc40cc7ce71654d69936f1f77bdc684d069615ffeb71566487cdd62c55bc9" + 86 | "5688452cb1857c91fd6cc0c7506f974ff4274a88b768f5e332b64933cabc9ef5" + 87 | "2204e62f8682c177d5c7aa6e94e66125ad7a42eb9352e6af1ea6478e92599454" + 88 | "65bc54fed2b45317713f7caa98cbd28a14c4c7fabe8689e735985e3fa6bd7ca8" + 89 | "bda58bee1b3cba48cb0d1508c79c23d48413b3dc296aabf5291288783ff037ef"; 90 | var PUBLIC_CIPHERTEXT_NP_HEX = 91 | "51b965fd83d619f9c3fec28330c7564c90439da62ec962f0c22df2e8e5bf9e52" + 92 | "712755d4bb004a0dcfd8e6e0acc92553805c11158b3e36a3f74d4da6574514e8" + 93 | "10313bcbe601fe43c7bb74872ab1a252ea78db775175662ed20baf05e870a265" + 94 | "1a06afdec90e9bd7a21cba0282ae0ca07e82898bcfbcc162d4f6780f5db89216" + 95 | "7a85bb5d26afc7551f356b054bb1ebb312fa02e212b2d0a751964a6ca790d6dd" + 96 | "424df1beb5e7887ae8498070955ba65777b02c7e47ebbfdfc81a54b6fa506869" + 97 | "29cf603b19cbaf1f32ad795ee8b6619b3dd8764626483425334cc892afc9e6e2" + 98 | "ca371a850ae29336f559da16acc250600febcbd57524ccc0f181c995ce3f74ba"; 99 | var PLAINTEXT_SHA256 = 100 | "44d7b7069244377863405b7c6a8c1e0fde6c68f02631668e41de3e9503429dcb"; 101 | var PLAINTEXT_SHA256_SIGNATURE = 102 | "98349f92a91a2de46a897901a54b395a85fb618ac904d3d01d3c37d54fbdaba5" + 103 | "f2f17fbcd45b9237b0b2f63398d5ab113c67cf3a356d30a14eeff9b53b0e7bef" + 104 | "8b347d835cb60550b082d86293f5fb3354a1d6d85ff19f1c6696683dc3ff55d6" + 105 | "72b88f3d365b0d56a65e3f974a795feee84b2097646673ccb4336950c04a3ba9" + 106 | "bd189c9ae03ee824bd5a70a5c40c6ea0b124f4256a18a054d175e339c0f4fdfd" + 107 | "32edacdec17940343889b2f940edf10c9da338db59972735049eea77632ff895" + 108 | "53d42ded904959718c9cf5e43a50a315ab860be64d0bd4ef69bd01154d2108a9" + 109 | "5127b0de26318d8b0c87eace03502c0cbfbe4683c5da7cf30bce3a386534b302"; 110 | 111 | var SSH_PUBLIC_KEY = 112 | new Buffer(SSH_PUBLIC_KEY_FILE.toString(UTF8).slice(8), BASE64); 113 | var SSH_PUBLIC_KEY_FINGERPRINT_HEX = "e7738e886aaf6f0301d62d459a892dc3"; 114 | 115 | var FAKE_SHA256_TO_SIGN = 116 | "0123456789abcdef00112233445566778899aabbccddeeff9876543210fedcba"; 117 | var FAKE_SHA256_SIGNATURE = 118 | "023fad1e5a94d417fc81ef477cc307578451791a87458794f993b9c8acd680aa" + 119 | "0458532349fafec4df9351f6962b656b6c71fe977ec9d9c4cc2956a0d22ab9c8" + 120 | "3a3639148d0bfe2a1d606868bee0b5a0c0d7b2bfaf80d4ed35d31f22733812dd" + 121 | "454e8beb119c935f250000f44eeefd61d45bffb3aa42bcb8be6eb5331dc83a14" + 122 | "bfc4df1dd2120f1d1b8539bd28cd4ebb6f3cf9439279278f5d472b75bff73c1b" + 123 | "9b7cf713f311e3e25d7b0bfa0ee25f25f78c06335d8440e6a96318d0246c1581" + 124 | "31c351b661694cfd688cd12f16db60ca496ca75338830d98dd1545ca835832d0" + 125 | "15398a8dbc55eccb5c95fc9e825960ebd99b9b614e18fe0284a2def94cfe9aba"; 126 | 127 | var PRIVATE_KEY_COMPONENTS = { 128 | modulus: new Buffer('4Vc173072M53tIJA/RR5U2IbLgesAVCGUTU2WmouBKSs5XmW64av0ezOAzAOFG4oriOvHzGoHBWIFIH7KQ290AWFUkQ6RdDpsRpuMMNCzo7Bx/9GUC57Bb2xzUHICYmJCx0BUYvQEcHrmTQkZ8N3zGNRoXzubKuye0swAGykN78=', 'base64'), 129 | exponent: new Buffer('AQAB', 'base64'), 130 | p: new Buffer('9jJD7kB+j+YcBbMtJq+M3wN/l9C9N9o8o2o2pIDPWZ9l5z3mC/0fwsdIdaAZHtwj6yjWkYUQqwcFMzFMd4JJuw==', 'base64'), 131 | q: new Buffer('6lBXfCfsiPWzLe6klV+vFaDl40x53AMy+pL0VE/f8GSqHwGPL1q1aa2AtiSCD505g6vq934V3K/KDK2THug3zQ==', 'base64'), 132 | dp: new Buffer('HnGIxOVkwRaLUIkfhDEhElK0bGl7fHSYGvz/VMg427RCPZ4B3Gmoi8VoyGLLuG0wY9vg7I2vyfZMRlBKTFzoEQ==', 'base64'), 133 | dq: new Buffer('Ieh2gJUWect0npUGZEdwguTB397VU61y1yglC35zncozhEEpg2TRE/XzxmgKGlBaXl+mpSIt773Qs3z66WIZkQ==', 'base64'), 134 | inverseQ: new Buffer('3inB7CsdrAdr3Isflf3OiYvzKTuRuhGC6TYFpk0P4o8Q1ucyjACWKC1gnvDYqqkdkwkfAg6Dxh3ZP1s9koxbvw==', 'base64'), 135 | d: new Buffer('OPTnMBpyZxGA1kJaFN348KeKgS71PfvRh3Mwwte1u/y3quT5zZxkkmYNiGa8GFPjumhQmkzd1gNnOu/DwRO1Fwbp0bfqwCLnd2Lz400ZrwN/S+hlzu+YR1rKOH7C7q/BeAm/hGVejsmkl16WgEpkqTzo11566Gat9UWDAw7C1Zk=', 'base64'), 136 | }; 137 | 138 | // From ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip 139 | 140 | var PSS_MODULUS_HEX = 141 | "a2ba40ee07e3b2bd2f02ce227f36a195024486e49c19cb41bbbdfbba98b22b0e"+ 142 | "577c2eeaffa20d883a76e65e394c69d4b3c05a1e8fadda27edb2a42bc000fe88"+ 143 | "8b9b32c22d15add0cd76b3e7936e19955b220dd17d4ea904b1ec102b2e4de775"+ 144 | "1222aa99151024c7cb41cc5ea21d00eeb41f7c800834d2c6e06bce3bce7ea9a5"; 145 | 146 | var PSS_M_HEX = 147 | "859eef2fd78aca00308bdc471193bf55bf9d78db8f8a672b484634f3c9c26e64"+ 148 | "78ae10260fe0dd8c082e53a5293af2173cd50c6d5d354febf78b26021c25c027"+ 149 | "12e78cd4694c9f469777e451e7f8e9e04cd3739c6bbfedae487fb55644e9ca74"+ 150 | "ff77a53cb729802f6ed4a5ffa8ba159890fc"; 151 | 152 | var PSS_MHASH_HEX = 153 | "37b66ae0445843353d47ecb0b4fd14c110e62d6a"; 154 | 155 | var PSS_EM_HEX = 156 | "66e4672e836ad121ba244bed6576b867d9a447c28a6e66a5b87dee7fbc7e65af" + 157 | "5057f86fae8984d9ba7f969ad6fe02a4d75f7445fefdd85b6d3a477c28d24ba1" + 158 | "e3756f792dd1dce8ca94440ecb5279ecd3183a311fc896da1cb39311af37ea4a" + 159 | "75e24bdbfd5c1da0de7cecdf1a896f9d8bc816d97cd7a2c43bad546fbe8cfebc"; 160 | 161 | var PSS_S_HEX = 162 | "8daa627d3de7595d63056c7ec659e54406f10610128baae821c8b2a0f3936d54" + 163 | "dc3bdce46689f6b7951bb18e840542769718d5715d210d85efbb596192032c42" + 164 | "be4c29972c856275eb6d5a45f05f51876fc6743deddd28caec9bb30ea99e02c3" + 165 | "488269604fe497f74ccd7c7fca1671897123cbd30def5d54a2b5536ad90a747e"; 166 | 167 | var PSS_PUBLIC_KEY = fs.readFileSync(__dirname + "/pss_tv.pub"); 168 | 169 | /* 170 | * Exported bindings 171 | */ 172 | 173 | module.exports = { 174 | BASE64: BASE64, 175 | BINARY: BINARY, 176 | HEX: HEX, 177 | SHA1: SHA1, 178 | SHA256: SHA256, 179 | UTF8: UTF8, 180 | DES_EDE3_CBC: DES_EDE3_CBC, 181 | 182 | EXPONENT_HEX: EXPONENT_HEX, 183 | FAKE_SHA256_TO_SIGN: FAKE_SHA256_TO_SIGN, 184 | FAKE_SHA256_SIGNATURE: FAKE_SHA256_SIGNATURE, 185 | MODULUS_HEX: MODULUS_HEX, 186 | PASSWORD: PASSWORD, 187 | PASS_PRIVATE_KEY: PASS_PRIVATE_KEY, 188 | PLAINTEXT: PLAINTEXT, 189 | PLAINTEXT_PADDED: PLAINTEXT_PADDED, 190 | PLAINTEXT_SHA256: PLAINTEXT_SHA256, 191 | PLAINTEXT_SHA256_SIGNATURE: PLAINTEXT_SHA256_SIGNATURE, 192 | PRIVATE_CIPHERTEXT_HEX: PRIVATE_CIPHERTEXT_HEX, 193 | PRIVATE_OLD_PAD_CIPHER_HEX: PRIVATE_OLD_PAD_CIPHER_HEX, 194 | PRIVATE_KEY: PRIVATE_KEY, 195 | PRIVATE_KEY_2: PRIVATE_KEY_2, 196 | PRIVATE_KEY_3: PRIVATE_KEY_3, 197 | PUBLIC_CIPHERTEXT_HEX: PUBLIC_CIPHERTEXT_HEX, 198 | PUBLIC_CIPHERTEXT_NP_HEX: PUBLIC_CIPHERTEXT_NP_HEX, 199 | PUBLIC_KEY: PUBLIC_KEY, 200 | PUBLIC_KEY_2: PUBLIC_KEY_2, 201 | SSH_PUBLIC_KEY: SSH_PUBLIC_KEY, 202 | SSH_PUBLIC_KEY_FINGERPRINT_HEX: SSH_PUBLIC_KEY_FINGERPRINT_HEX, 203 | PRIVATE_KEY_COMPONENTS: PRIVATE_KEY_COMPONENTS, 204 | PSS_MODULUS_HEX: PSS_MODULUS_HEX, 205 | PSS_M_HEX: PSS_M_HEX, 206 | PSS_MHASH_HEX: PSS_MHASH_HEX, 207 | PSS_EM_HEX: PSS_EM_HEX, 208 | PSS_S_HEX: PSS_S_HEX, 209 | PSS_PUBLIC_KEY: PSS_PUBLIC_KEY, 210 | 211 | RsaWrap: ursaNative.RsaWrap, 212 | 213 | ursa: ursa, 214 | ursaNative: ursaNative 215 | }; 216 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Obvious Corporation. 2 | 3 | /* 4 | * Tests of ursa 5 | */ 6 | 7 | /* 8 | * Modules used 9 | */ 10 | 11 | "use strict"; 12 | 13 | var assert = require("assert"); 14 | 15 | var fixture = require("./fixture"); 16 | var ursa = fixture.ursa; 17 | 18 | /** 19 | * Asserts that two strings are equal, ignoring Windows newline differences 20 | */ 21 | function assertStringEqual(actual, expected, message) { 22 | assert.equal(actual.replace(/\r\n/g, '\n'), expected.replace(/\r\n/g, '\n'), message); 23 | } 24 | 25 | /* 26 | * Helper functions 27 | */ 28 | 29 | function test_getExponent(key) { 30 | var buf = key.getExponent(); 31 | assert.equal(buf.toString(fixture.HEX), fixture.EXPONENT_HEX); 32 | 33 | var result = key.getExponent(fixture.HEX); 34 | assert.equal(result, fixture.EXPONENT_HEX); 35 | 36 | result = key.getExponent(fixture.BASE64); 37 | assert.equal(result, buf.toString(fixture.BASE64)); 38 | 39 | result = key.getExponent(fixture.BINARY); 40 | assert.equal(result, buf.toString(fixture.BINARY)); 41 | 42 | result = key.getExponent(fixture.UTF8); 43 | assert.equal(result, buf.toString(fixture.UTF8)); 44 | } 45 | 46 | function test_getModulus(key) { 47 | var buf = key.getModulus(); 48 | assert.equal(buf.toString(fixture.HEX), fixture.MODULUS_HEX); 49 | 50 | var result = key.getModulus(fixture.HEX); 51 | assert.equal(result, fixture.MODULUS_HEX); 52 | 53 | result = key.getModulus(fixture.BASE64); 54 | assert.equal(result, buf.toString(fixture.BASE64)); 55 | 56 | result = key.getModulus(fixture.BINARY); 57 | assert.equal(result, buf.toString(fixture.BINARY)); 58 | 59 | result = key.getModulus(fixture.UTF8); 60 | assert.equal(result, buf.toString(fixture.UTF8)); 61 | } 62 | 63 | function test_toPublicPem(key) { 64 | var keyString = fixture.PUBLIC_KEY.toString(fixture.UTF8); 65 | var result = key.toPublicPem().toString(fixture.UTF8); 66 | assertStringEqual(result, keyString); 67 | 68 | result = key.toPublicPem(fixture.UTF8); 69 | assertStringEqual(result, keyString); 70 | } 71 | 72 | function test_toPublicSsh(key) { 73 | var keyString = fixture.SSH_PUBLIC_KEY.toString(fixture.BASE64); 74 | var result = key.toPublicSsh().toString(fixture.BASE64); 75 | assert.equal(result, keyString); 76 | 77 | result = key.toPublicSsh(fixture.BASE64); 78 | assert.equal(result, keyString); 79 | } 80 | 81 | function test_toPublicSshFingerprint(key) { 82 | var result = key.toPublicSshFingerprint().toString(fixture.HEX); 83 | assert.equal(result, fixture.SSH_PUBLIC_KEY_FINGERPRINT_HEX); 84 | 85 | result = key.toPublicSshFingerprint(fixture.HEX); 86 | assert.equal(result, fixture.SSH_PUBLIC_KEY_FINGERPRINT_HEX); 87 | } 88 | 89 | function test_encrypt(key) { 90 | // The sanest way to test this is to do a round trip. 91 | var privKey = ursa.createPrivateKey(fixture.PRIVATE_KEY) 92 | var encoded = key.encrypt(new Buffer(fixture.PLAINTEXT, fixture.UTF8)); 93 | var decoded = privKey.decrypt(encoded, undefined, fixture.UTF8); 94 | assert.equal(decoded, fixture.PLAINTEXT); 95 | 96 | encoded = key.encrypt(fixture.PLAINTEXT, fixture.UTF8, fixture.BASE64); 97 | decoded = privKey.decrypt(encoded, fixture.BASE64, fixture.UTF8); 98 | assert.equal(decoded, fixture.PLAINTEXT); 99 | 100 | encoded = key.encrypt(fixture.PLAINTEXT, undefined, fixture.HEX); 101 | decoded = privKey.decrypt(encoded, fixture.HEX, fixture.UTF8); 102 | assert.equal(decoded, fixture.PLAINTEXT); 103 | } 104 | 105 | function test_publicDecrypt(key) { 106 | var encoded = new Buffer(fixture.PUBLIC_CIPHERTEXT_HEX, fixture.HEX); 107 | var decoded = key.publicDecrypt(encoded).toString(fixture.UTF8); 108 | assert.equal(decoded, fixture.PLAINTEXT); 109 | 110 | decoded = key.publicDecrypt(fixture.PUBLIC_CIPHERTEXT_HEX, fixture.HEX, 111 | fixture.UTF8); 112 | assert.equal(decoded, fixture.PLAINTEXT); 113 | 114 | decoded = key.publicDecrypt(fixture.PUBLIC_CIPHERTEXT_NP_HEX, fixture.HEX, 115 | fixture.UTF8, ursa.RSA_NO_PADDING); 116 | assert.equal(decoded, fixture.PLAINTEXT_PADDED); 117 | } 118 | 119 | function test_verify(key) { 120 | assert.equal(key.verify(fixture.SHA256, fixture.PLAINTEXT_SHA256, 121 | fixture.PLAINTEXT_SHA256_SIGNATURE, 122 | fixture.HEX), true); 123 | 124 | var hash = new Buffer(fixture.PLAINTEXT_SHA256, fixture.HEX); 125 | var sig = new Buffer(fixture.PLAINTEXT_SHA256_SIGNATURE, fixture.HEX); 126 | assert.equal(key.verify(fixture.SHA256, hash, sig), true); 127 | } 128 | 129 | function test_hashAndVerify(key) { 130 | assert.equal(key.hashAndVerify(fixture.SHA256, 131 | new Buffer(fixture.PLAINTEXT, fixture.UTF8), 132 | fixture.PLAINTEXT_SHA256_SIGNATURE, 133 | fixture.HEX), 134 | true); 135 | 136 | var key2 = ursa.createPublicKeyFromComponents( 137 | new Buffer(fixture.PSS_MODULUS_HEX, fixture.HEX), 138 | new Buffer(fixture.EXPONENT_HEX, fixture.HEX)); 139 | 140 | assert.equal(key2.hashAndVerify(fixture.SHA1, 141 | fixture.PSS_M_HEX, 142 | fixture.PSS_S_HEX, 143 | fixture.HEX, 144 | true, 145 | ursa.RSA_PKCS1_SALT_LEN_HLEN), 146 | true); 147 | 148 | assert.equal(key2.hashAndVerify(fixture.SHA1, 149 | fixture.PSS_M_HEX, 150 | fixture.PSS_S_HEX, 151 | fixture.HEX, 152 | true), 153 | true); 154 | } 155 | 156 | function testPublicKeyMethods(key) { 157 | test_getExponent(key); 158 | test_getModulus(key); 159 | test_toPublicPem(key); 160 | test_toPublicSsh(key); 161 | test_toPublicSshFingerprint(key); 162 | test_encrypt(key); 163 | test_publicDecrypt(key); 164 | test_verify(key); 165 | test_hashAndVerify(key); 166 | } 167 | 168 | function test_toPrivatePem(key) { 169 | var keyString = fixture.PRIVATE_KEY.toString(fixture.UTF8); 170 | var result = key.toPrivatePem().toString(fixture.UTF8); 171 | assertStringEqual(result, keyString); 172 | 173 | result = key.toPrivatePem(fixture.UTF8); 174 | assertStringEqual(result, keyString); 175 | } 176 | 177 | function test_toEncryptedPrivatePem(key) { 178 | var password = fixture.PASSWORD.toString(fixture.UTF8); 179 | var cipher = fixture.DES_EDE3_CBC; 180 | 181 | var keyString = fixture.PASS_PRIVATE_KEY.toString(fixture.UTF8); 182 | var pem = key.toEncryptedPrivatePem(password, cipher).toString(fixture.UTF8); 183 | 184 | var plainTextKey = ursa.createPrivateKey(pem, password); 185 | assertStringEqual(plainTextKey.toPrivatePem().toString(), fixture.PRIVATE_KEY.toString()); 186 | 187 | 188 | pem = key.toEncryptedPrivatePem(password, cipher, fixture.UTF8).toString(fixture.UTF8); 189 | 190 | plainTextKey = ursa.createPrivateKey(pem, password); 191 | assertStringEqual(plainTextKey.toPrivatePem().toString(), fixture.PRIVATE_KEY.toString()); 192 | } 193 | 194 | function test_decrypt(key) { 195 | var encoded = new Buffer(fixture.PRIVATE_CIPHERTEXT_HEX, fixture.HEX); 196 | var decoded = key.decrypt(encoded).toString(fixture.UTF8); 197 | assert.equal(decoded, fixture.PLAINTEXT); 198 | 199 | decoded = key.decrypt(fixture.PRIVATE_CIPHERTEXT_HEX, fixture.HEX, 200 | fixture.UTF8); 201 | assert.equal(decoded, fixture.PLAINTEXT); 202 | } 203 | 204 | function test_privateEncrypt(key) { 205 | var encoded = key.privateEncrypt( 206 | new Buffer(fixture.PLAINTEXT, fixture.UTF8)).toString(fixture.HEX); 207 | assert.equal(encoded, fixture.PUBLIC_CIPHERTEXT_HEX); 208 | 209 | encoded = key.privateEncrypt(fixture.PLAINTEXT, fixture.UTF8, fixture.HEX); 210 | assert.equal(encoded, fixture.PUBLIC_CIPHERTEXT_HEX); 211 | 212 | encoded = key.privateEncrypt(fixture.PLAINTEXT, undefined, fixture.HEX); 213 | assert.equal(encoded, fixture.PUBLIC_CIPHERTEXT_HEX); 214 | 215 | encoded = key.privateEncrypt(fixture.PLAINTEXT_PADDED, fixture.UTF8, fixture.HEX, ursa.RSA_NO_PADDING); 216 | assert.equal(encoded, fixture.PUBLIC_CIPHERTEXT_NP_HEX); 217 | } 218 | 219 | function test_sign(key) { 220 | var sig = key.sign(fixture.SHA256, 221 | fixture.PLAINTEXT_SHA256, fixture.HEX, 222 | fixture.BASE64); 223 | sig = new Buffer(sig, fixture.BASE64); 224 | assert.equal(sig.toString(fixture.HEX), fixture.PLAINTEXT_SHA256_SIGNATURE); 225 | 226 | var buf = new Buffer(fixture.PLAINTEXT_SHA256, fixture.HEX); 227 | sig = key.sign(fixture.SHA256, buf, undefined, fixture.HEX); 228 | assert.equal(sig, fixture.PLAINTEXT_SHA256_SIGNATURE); 229 | } 230 | 231 | function test_hashAndSign(key) { 232 | var sig = key.hashAndSign(fixture.SHA256, fixture.PLAINTEXT, 233 | fixture.UTF8, fixture.HEX); 234 | assert.equal(sig, fixture.PLAINTEXT_SHA256_SIGNATURE); 235 | 236 | // PSS uses random salt so can't have a fixture 237 | 238 | var sig = key.hashAndSign(fixture.SHA256, fixture.PLAINTEXT, 239 | fixture.UTF8, fixture.HEX, 240 | true, ursa.RSA_PKCS1_SALT_LEN_MAX); 241 | 242 | assert.equal(key.hashAndVerify( 243 | fixture.SHA256, 244 | new Buffer(fixture.PLAINTEXT).toString(fixture.HEX), 245 | sig, 246 | fixture.HEX, 247 | true, 248 | ursa.RSA_PKCS1_SALT_LEN_MAX), 249 | true); 250 | } 251 | 252 | function testPrivateKeyMethods(key) { 253 | test_toPrivatePem(key); 254 | test_toEncryptedPrivatePem(key); 255 | test_decrypt(key); 256 | test_privateEncrypt(key); 257 | test_hashAndSign(key); 258 | test_sign(key); 259 | } 260 | 261 | describe('main', function() { 262 | it('Basics', function() { 263 | ursa.createPublicKey(fixture.PUBLIC_KEY); 264 | ursa.createPrivateKey(fixture.PRIVATE_KEY); 265 | ursa.createPrivateKey(fixture.PASS_PRIVATE_KEY, fixture.PASSWORD); 266 | ursa.generatePrivateKey(512); 267 | 268 | ursa.createPublicKey(fixture.PUBLIC_KEY.toString(fixture.UTF8)); 269 | ursa.createPrivateKey(fixture.PRIVATE_KEY.toString(fixture.BASE64), 270 | undefined, fixture.BASE64); 271 | }); 272 | 273 | it('Types', function() { 274 | var pub = ursa.createPublicKey(fixture.PUBLIC_KEY); 275 | var priv = ursa.createPrivateKey(fixture.PRIVATE_KEY); 276 | var msg; 277 | 278 | msg = "Problem with isKey()"; 279 | assert.equal(ursa.isKey(pub), true, msg); 280 | assert.equal(ursa.isKey(priv), true, msg); 281 | assert.equal(ursa.isKey(undefined), false, msg); 282 | assert.equal(ursa.isKey("x"), false, msg); 283 | 284 | msg = "Problem with isPublicKey()"; 285 | assert.equal(ursa.isPublicKey(pub), true, msg); 286 | assert.equal(ursa.isPublicKey(priv), false, msg); 287 | assert.equal(ursa.isPublicKey(undefined), false, msg); 288 | assert.equal(ursa.isPublicKey("x"), false, msg); 289 | 290 | msg = "Problem with isPrivateKey()"; 291 | assert.equal(ursa.isPrivateKey(pub), false, msg); 292 | assert.equal(ursa.isPrivateKey(priv), true, msg); 293 | assert.equal(ursa.isPrivateKey(undefined), false, msg); 294 | assert.equal(ursa.isPrivateKey("x"), false, msg); 295 | 296 | assert.doesNotThrow(function () { ursa.assertKey(pub); }); 297 | assert.doesNotThrow(function () { ursa.assertKey(priv); }); 298 | assert.throws(function () { ursa.assertKey(undefined); }); 299 | assert.throws(function () { ursa.assertKey("x"); }); 300 | 301 | assert.doesNotThrow(function () { ursa.assertPublicKey(pub); }); 302 | assert.throws(function () { ursa.assertPublicKey(priv); }); 303 | assert.throws(function () { ursa.assertPublicKey(undefined); }); 304 | assert.throws(function () { ursa.assertPublicKey("x"); }); 305 | 306 | assert.throws(function () { ursa.assertPrivateKey(pub); }); 307 | assert.doesNotThrow(function () { ursa.assertPrivateKey(priv); }); 308 | assert.throws(function () { ursa.assertPrivateKey(undefined); }); 309 | assert.throws(function () { ursa.assertPrivateKey("x"); }); 310 | }); 311 | 312 | it('createKey', function() { 313 | var priv = ursa.createKey(fixture.PRIVATE_KEY); 314 | assert(ursa.isPrivateKey(priv), true); 315 | 316 | var pub = ursa.createKey(fixture.PUBLIC_KEY); 317 | assert(ursa.isPublicKey(pub), true); 318 | 319 | function f1() { 320 | ursa.createKey("yo there"); 321 | } 322 | assert.throws(f1, /Not a key\./); 323 | }); 324 | 325 | it('createPrivateKeyFromComponents', function() { 326 | var privFromComponents = ursa.createPrivateKeyFromComponents( 327 | fixture.PRIVATE_KEY_COMPONENTS.modulus, 328 | fixture.PRIVATE_KEY_COMPONENTS.exponent, 329 | fixture.PRIVATE_KEY_COMPONENTS.p, 330 | fixture.PRIVATE_KEY_COMPONENTS.q, 331 | fixture.PRIVATE_KEY_COMPONENTS.dp, 332 | fixture.PRIVATE_KEY_COMPONENTS.dq, 333 | fixture.PRIVATE_KEY_COMPONENTS.inverseQ, 334 | fixture.PRIVATE_KEY_COMPONENTS.d); 335 | 336 | assert(ursa.isPrivateKey(privFromComponents), true); 337 | 338 | var privFromPem = ursa.createPrivateKey(fixture.PRIVATE_KEY_3); 339 | 340 | assert.equal(privFromComponents.toPrivatePem('utf8'), privFromPem.toPrivatePem('utf8')); 341 | }); 342 | 343 | it('createPublicKeyFromComponents', function() { 344 | var pubFromComponents = ursa.createPublicKeyFromComponents( 345 | new Buffer(fixture.PSS_MODULUS_HEX, fixture.HEX), 346 | new Buffer(fixture.EXPONENT_HEX, fixture.HEX)); 347 | 348 | assert(ursa.isPublicKey(pubFromComponents), true); 349 | 350 | var pubFromPem = ursa.createPublicKey(fixture.PSS_PUBLIC_KEY); 351 | 352 | assert.equal(pubFromComponents.toPublicPem('utf8'), 353 | pubFromPem.toPublicPem('utf8')); 354 | }); 355 | 356 | it('fail_createPublicKey', function() { 357 | // This is mostly tested at the native level. This just tests the 358 | // extra failures added at the high level. 359 | function f1() { 360 | ursa.createPublicKey(fixture.PRIVATE_KEY); 361 | } 362 | assert.throws(f1, /Not a public key\./); 363 | }); 364 | 365 | it('fail_createPrivateKey', function() { 366 | // This is mostly tested at the native level. This just tests the 367 | // extra failures added at the high level. 368 | function f1() { 369 | ursa.createPrivateKey(fixture.PUBLIC_KEY); 370 | } 371 | assert.throws(f1, /Not a private key\./); 372 | }); 373 | 374 | it('coerceKey', function() { 375 | var priv = ursa.coerceKey(fixture.PRIVATE_KEY); 376 | assert(ursa.isPrivateKey(priv), true); 377 | 378 | priv = ursa.coerceKey(fixture.PRIVATE_KEY.toString()); 379 | assert(ursa.isPrivateKey(priv), true); 380 | 381 | var pub = ursa.coerceKey(fixture.PUBLIC_KEY); 382 | assert(ursa.isPublicKey(pub), true); 383 | 384 | pub = ursa.coerceKey(fixture.PUBLIC_KEY.toString()); 385 | assert(ursa.isPublicKey(pub), true); 386 | 387 | assert.equal(ursa.coerceKey(priv), priv); 388 | assert.equal(ursa.coerceKey(pub), pub); 389 | }); 390 | 391 | it('coercePrivateKey', function() { 392 | var priv = ursa.coercePrivateKey(fixture.PRIVATE_KEY); 393 | assert(ursa.isPrivateKey(priv), true); 394 | 395 | priv = ursa.coercePrivateKey(fixture.PRIVATE_KEY.toString()); 396 | assert(ursa.isPrivateKey(priv), true); 397 | 398 | assert.equal(ursa.coercePrivateKey(priv), priv); 399 | }); 400 | 401 | it('coercePublicKey', function() { 402 | var pub = ursa.coercePublicKey(fixture.PUBLIC_KEY); 403 | assert(ursa.isPublicKey(pub), true); 404 | 405 | pub = ursa.coercePublicKey(fixture.PUBLIC_KEY.toString()); 406 | assert(ursa.isPublicKey(pub), true); 407 | 408 | assert.equal(ursa.coercePublicKey(pub), pub); 409 | }); 410 | 411 | it('fail_coerceKey', function() { 412 | function f1() { 413 | ursa.coerceKey("foo"); 414 | } 415 | assert.throws(f1, /Not a key/); 416 | 417 | function f2() { 418 | ursa.coerceKey(new Buffer(200)); 419 | } 420 | assert.throws(f2, /Not a key/); 421 | 422 | function f3() { 423 | ursa.coerceKey([]); 424 | } 425 | assert.throws(f3, /Not a key/); 426 | }); 427 | 428 | it('fail_coercePrivateKey', function() { 429 | function f1() { 430 | ursa.coercePrivateKey("foo"); 431 | } 432 | assert.throws(f1, /Not a private key/); 433 | 434 | function f2() { 435 | ursa.coercePrivateKey(new Buffer(200)); 436 | } 437 | assert.throws(f2, /Not a private key/); 438 | 439 | function f3() { 440 | ursa.coercePrivateKey([]); 441 | } 442 | assert.throws(f3, /Not a private key/); 443 | 444 | function f4() { 445 | ursa.coercePrivateKey(fixture.PUBLIC_KEY); 446 | } 447 | assert.throws(f4, /Not a private key/); 448 | 449 | function f5() { 450 | ursa.coercePrivateKey(fixture.PUBLIC_KEY.toString()); 451 | } 452 | assert.throws(f5, /Not a private key/); 453 | }); 454 | 455 | it('fail_coercePublicKey', function() { 456 | function f1() { 457 | ursa.coercePublicKey("foo"); 458 | } 459 | assert.throws(f1, /Not a public key/); 460 | 461 | function f2() { 462 | ursa.coercePublicKey(new Buffer(200)); 463 | } 464 | assert.throws(f2, /Not a public key/); 465 | 466 | function f3() { 467 | ursa.coercePublicKey([]); 468 | } 469 | assert.throws(f3, /Not a public key/); 470 | 471 | function f4() { 472 | ursa.coercePublicKey(fixture.PRIVATE_KEY); 473 | } 474 | assert.throws(f4, /Not a public key/); 475 | 476 | function f5() { 477 | ursa.coercePublicKey(fixture.PRIVATE_KEY.toString()); 478 | } 479 | assert.throws(f5, /Not a public key/); 480 | }); 481 | 482 | it('PublicKey', function() { 483 | var key = ursa.createPublicKey(fixture.PUBLIC_KEY); 484 | testPublicKeyMethods(key); 485 | }); 486 | 487 | it('PrivateKey', function() { 488 | var key = ursa.createPrivateKey(fixture.PRIVATE_KEY); 489 | testPublicKeyMethods(key); 490 | testPrivateKeyMethods(key); 491 | }); 492 | 493 | it('GeneratedKey', function() { 494 | // Just do a round trip. If that works, then it's safe to believe 495 | // the native tests (which are more comprehensive). 496 | var key = ursa.generatePrivateKey(); 497 | var encoded = key.encrypt(fixture.PLAINTEXT, fixture.UTF8); 498 | var decoded = key.decrypt(encoded, undefined, fixture.UTF8); 499 | assert.equal(decoded, fixture.PLAINTEXT); 500 | }); 501 | 502 | it('sshFingerprint', function() { 503 | var key = fixture.SSH_PUBLIC_KEY; 504 | var finger = ursa.sshFingerprint(fixture.SSH_PUBLIC_KEY); 505 | assert.equal(finger.toString(fixture.HEX), 506 | fixture.SSH_PUBLIC_KEY_FINGERPRINT_HEX); 507 | 508 | finger = ursa.sshFingerprint(fixture.SSH_PUBLIC_KEY, undefined, 509 | fixture.HEX); 510 | assert.equal(finger, fixture.SSH_PUBLIC_KEY_FINGERPRINT_HEX); 511 | 512 | finger = ursa.sshFingerprint( 513 | fixture.SSH_PUBLIC_KEY.toString(fixture.BASE64), 514 | fixture.BASE64, fixture.HEX); 515 | assert.equal(finger, fixture.SSH_PUBLIC_KEY_FINGERPRINT_HEX); 516 | }); 517 | 518 | it('equalKeys', function() { 519 | var pub = ursa.createPublicKey(fixture.PUBLIC_KEY); 520 | var priv = ursa.createPrivateKey(fixture.PRIVATE_KEY); 521 | var samePub = ursa.createPublicKey(fixture.PUBLIC_KEY); 522 | var samePriv = ursa.createPrivateKey(fixture.PRIVATE_KEY); 523 | var diffPub = ursa.createPublicKey(fixture.PUBLIC_KEY_2); 524 | var diffPriv = ursa.createPrivateKey(fixture.PRIVATE_KEY_2); 525 | 526 | assert.equal(ursa.equalKeys("1", "2"), false); 527 | assert.equal(ursa.equalKeys(123, 123), false); 528 | assert.equal(ursa.equalKeys(pub, null), false); 529 | assert.equal(ursa.equalKeys(true, pub), false); 530 | 531 | assert.equal(ursa.equalKeys(pub, pub), true); 532 | assert.equal(ursa.equalKeys(priv, priv), true); 533 | assert.equal(ursa.equalKeys(pub, priv), false); 534 | assert.equal(ursa.equalKeys(priv, pub), false); 535 | 536 | assert.equal(ursa.equalKeys(pub, samePub), true); 537 | assert.equal(ursa.equalKeys(priv, samePriv), true); 538 | 539 | assert.equal(ursa.equalKeys(pub, diffPub), false); 540 | assert.equal(ursa.equalKeys(priv, diffPriv), false); 541 | }); 542 | 543 | it('matchingPublicKeys', function() { 544 | var pub = ursa.createPublicKey(fixture.PUBLIC_KEY); 545 | var priv = ursa.createPrivateKey(fixture.PRIVATE_KEY); 546 | var samePub = ursa.createPublicKey(fixture.PUBLIC_KEY); 547 | var samePriv = ursa.createPrivateKey(fixture.PRIVATE_KEY); 548 | var diffPub = ursa.createPublicKey(fixture.PUBLIC_KEY_2); 549 | var diffPriv = ursa.createPrivateKey(fixture.PRIVATE_KEY_2); 550 | 551 | assert.equal(ursa.matchingPublicKeys("1", "2"), false); 552 | assert.equal(ursa.matchingPublicKeys(123, 123), false); 553 | assert.equal(ursa.matchingPublicKeys(pub, null), false); 554 | assert.equal(ursa.matchingPublicKeys(true, pub), false); 555 | 556 | assert.equal(ursa.matchingPublicKeys(pub, pub), true); 557 | assert.equal(ursa.matchingPublicKeys(priv, priv), true); 558 | assert.equal(ursa.matchingPublicKeys(pub, priv), true); 559 | assert.equal(ursa.matchingPublicKeys(priv, pub), true); 560 | 561 | assert.equal(ursa.matchingPublicKeys(pub, samePub), true); 562 | assert.equal(ursa.matchingPublicKeys(priv, samePriv), true); 563 | assert.equal(ursa.matchingPublicKeys(pub, samePriv), true); 564 | assert.equal(ursa.matchingPublicKeys(priv, samePub), true); 565 | 566 | assert.equal(ursa.matchingPublicKeys(pub, diffPub), false); 567 | assert.equal(ursa.matchingPublicKeys(pub, diffPriv), false); 568 | assert.equal(ursa.matchingPublicKeys(priv, diffPriv), false); 569 | assert.equal(ursa.matchingPublicKeys(priv, diffPub), false); 570 | }); 571 | 572 | it('Signer', function() { 573 | var key = ursa.createPrivateKey(fixture.PRIVATE_KEY); 574 | var signer = ursa.createSigner(fixture.SHA256); 575 | 576 | var ret = signer.update(fixture.PLAINTEXT, fixture.UTF8); 577 | assert.equal(ret === signer, true); 578 | 579 | var sig = signer.sign(key, fixture.HEX); 580 | 581 | assert.equal(sig, fixture.PLAINTEXT_SHA256_SIGNATURE); 582 | }); 583 | 584 | it('Verifier', function() { 585 | var key = ursa.createPublicKey(fixture.PUBLIC_KEY); 586 | var verifier = ursa.createVerifier(fixture.SHA256); 587 | 588 | var ret = verifier.update(fixture.PLAINTEXT, fixture.UTF8); 589 | assert.equal(ret === verifier, true); 590 | 591 | 592 | assert.equal(verifier.verify(key, fixture.PLAINTEXT_SHA256_SIGNATURE, 593 | fixture.HEX), 594 | true); 595 | 596 | var verifier = ursa.createVerifier(fixture.SHA256); 597 | verifier.update(new Buffer(fixture.PLAINTEXT, fixture.UTF8)); 598 | var sigBuf = new Buffer(fixture.PLAINTEXT_SHA256_SIGNATURE, fixture.HEX); 599 | assert.equal(verifier.verify(key, sigBuf), true); 600 | }); 601 | 602 | it('openSshPublicKey', function() { 603 | var sshKey = ursa.openSshPublicKey(fixture.SSH_PUBLIC_KEY), 604 | pubKey = ursa.createPublicKey(fixture.PUBLIC_KEY); 605 | 606 | assert.equal(ursa.equalKeys(sshKey, pubKey), true); 607 | }); 608 | }) 609 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | URSA - RSA public/private key OpenSSL bindings for Node.js 2 | ==== 3 | 4 | 5 | [![Build Status](https://travis-ci.org/JoshKaufman/ursa.svg?branch=master)](https://travis-ci.org/JoshKaufman/ursa) 6 | 7 | > NOTE: This package was transfered from [Medium](https://github.com/Medium) and [NodePrime](https://github.com/NodePrime) to [quartzjer](https://github.com/quartzjer) to [JoshKaufman](https://github.com/joshkaufman) on 8-2017. Pull requests are welcomed to help maintain it. 8 | 9 | -- 10 | 11 | This Node module provides a fairly complete set of wrappers for the 12 | RSA public/private key crypto functionality of OpenSSL. 13 | 14 | It is being actively developed for node.js 0.8.* through 0.12.* and io.js. If you find it doesn't work for you, please file a bug (see below). 15 | 16 | It has been tested on Windows by [SLaks](https://github.com/SLaks). (see [additional installation requirements](#windows-install)) 17 | 18 | Table of Contents 19 | ----------------- 20 | - [Simple Encrypt / Decrypt Example](#simple-encrypt--decrypt-example) 21 | - [Building and Installing](#building-and-installing) 22 | - [Usage](#usage) 23 | - [Top-level Exports](#top-level-exports) 24 | - [Public Key Methods](#public-key-methods) 25 | - [Private Key Methods](#private-key-methods) 26 | - [Signer Methods](#signer-methods) 27 | - [Verifier Methods](#verifier-methods) 28 | - [Constants](#constants) 29 | - [Contributing](#contributing) 30 | - [Authors](#authors) 31 | - [License](#license) 32 | - [Related Repos](#other-repos-that-may-be-of-interest) 33 | 34 | 35 | 36 | 37 | Simple Encrypt / Decrypt Example 38 | -------------------------------- 39 | 40 | ```javascript 41 | // openssl genrsa -out certs/server/my-server.key.pem 2048 42 | // openssl rsa -in certs/server/my-server.key.pem -pubout -out certs/client/my-server.pub 43 | 44 | 'use strict'; 45 | 46 | var fs = require('fs') 47 | , ursa = require('ursa') 48 | , crt 49 | , key 50 | , msg 51 | ; 52 | 53 | key = ursa.createPrivateKey(fs.readFileSync('./certs/server/my-server.key.pem')); 54 | crt = ursa.createPublicKey(fs.readFileSync('./certs/client/my-server.pub')); 55 | 56 | console.log('Encrypt with Public'); 57 | msg = crt.encrypt("Everything is going to be 200 OK", 'utf8', 'base64'); 58 | console.log('encrypted', msg, '\n'); 59 | 60 | console.log('Decrypt with Private'); 61 | msg = key.decrypt(msg, 'base64', 'utf8'); 62 | console.log('decrypted', msg, '\n'); 63 | 64 | console.log('############################################'); 65 | console.log('Reverse Public -> Private, Private -> Public'); 66 | console.log('############################################\n'); 67 | 68 | console.log('Encrypt with Private (called public)'); 69 | msg = key.privateEncrypt("Everything is going to be 200 OK", 'utf8', 'base64'); 70 | console.log('encrypted', msg, '\n'); 71 | 72 | console.log('Decrypt with Public (called private)'); 73 | msg = crt.publicDecrypt(msg, 'base64', 'utf8'); 74 | console.log('decrypted', msg, '\n'); 75 | ``` 76 | 77 | 78 | Building and Installing 79 | ----------------------- 80 | 81 | ```shell 82 | npm install ursa 83 | ``` 84 | 85 | Or grab the source and 86 | 87 | ```shell 88 | npm install 89 | ``` 90 | 91 | Testing 92 | ------- 93 | 94 | ```shell 95 | npm test 96 | ``` 97 | 98 | Or 99 | 100 | ```shell 101 | node ./test/test.js 102 | ``` 103 | 104 | On Windows, you'll need to install some dependencies first: 105 | - [OpenSSL](http://slproweb.com/products/Win32OpenSSL.html) (normal, not light) 106 | in the same bitness as your Node.js installation. 107 | - OpenSSL must be installed in the a specific install directory (`C:\OpenSSL-Win32` or `C:\OpenSSL-Win64`) 108 | - If you get `Error: The specified module could not be found.`, copy `libeay32.dll` from the OpenSSL bin directory to this module's bin directory, or to Windows\System32. 109 | - [node-gyp](https://github.com/nodejs/node-gyp) (`npm install -g node-gyp`) 110 | - You will need [python 2.7](http://www.python.org/download/releases/2.7.3#download) and a compatible version 111 | Visual Studio installed first. Even with that, `node-gyp` installation or use can have 112 | issues on Windows. The `node-gyp` [README file](https://github.com/nodejs/node-gyp) has detailed instructions 113 | if you have difficulties. [This post](https://www.robertkehoe.com/2015/03/fix-node-gyp-rebuild-error-on-windows/) 114 | is also a good reference. 115 | 116 | Usage 117 | ----- 118 | 119 | This library aims to be convenient to use, allowing one to pass in and 120 | get back regular string objects. However, it is also meant to be reasonably 121 | easy to use efficiently, allowing one to pass in and get back Buffer 122 | objects. Using Buffers is always the more efficient option. 123 | 124 | All methods that can deal with strings take one or more arguments indicating 125 | the encoding to use when interpreting an argument or generating a result. 126 | These are limited to the usual encoding names that are valid for use with 127 | Buffers: `base64` `binary` `hex` and `utf8`. If an encoding is left undefined 128 | and the argument is a string, then the encoding is *always* assumed to be 129 | `utf8`. If an argument is a Buffer, then the encoding (if defined at all) 130 | is ignored. An undefined output encoding is *always* interpreted as a request 131 | for a Buffer result. 132 | 133 | The library knows how to read and output PEM format files for both 134 | public and private keys, and it can generate new private keys (aka 135 | keypairs). 136 | 137 | The usual public-encryption / private-decryption operations by default 138 | use padding mode `RSA_PKCS1_OAEP_PADDING`, which is the recommended 139 | mode for all new applications (as of this writing). Note that this mode 140 | builds-in a random element into every encryption operation, making it 141 | unnecessary to waste time or effort adding randomness in at a higher layer. 142 | This default may be overridden to use the older mode `RSA_PKCS1_PADDING` 143 | if needed. 144 | 145 | The less well-understood private-encryption / public-decryption operations 146 | (used for building signature mechanisms) by default use padding 147 | mode `RSA_PKCS1_PADDING`. This doesn't build in any randomness (but that's 148 | not usually a problem for applications that use these operations). This 149 | default may be overridden to use `RSA_NO_PADDING` if needed. 150 | 151 | See the doc comments and tests for the excruciating details, but here's 152 | a quick rundown of the available top-level exports and instance methods: 153 | 154 | Top-Level Exports 155 | ----------------- 156 | 157 | ### ursa.createPrivateKey(pem, password, encoding) 158 | 159 | Create and return a private key (aka a keypair) read in from the given 160 | PEM-format file. If defined, the given password is used to decrypt 161 | the PEM file. 162 | 163 | The encoding, if specified, applies to both other arguments. 164 | 165 | See "Public Key Methods" below for more details. 166 | 167 | ### ursa.createPrivateKeyFromComponents(modulus, exponent, p, q, dp, dq, inverseQ, d) 168 | 169 | Create and return a private key from the given components. 170 | 171 | ### ursa.createPublicKeyFromComponents(modulus, exponent) 172 | 173 | Create and return a public key from the given components. 174 | 175 | ### ursa.assertKey(obj) 176 | 177 | Convenient shorthand for `assert(ursa.isKey(obj))`. 178 | 179 | ### ursa.assertPrivateKey(obj) 180 | 181 | Convenient shorthand for `assert(ursa.isPrivateKey(obj))`. 182 | 183 | ### ursa.assertPublicKey(obj) 184 | 185 | Convenient shorthand for `assert(ursa.isPublicKey(obj))`. 186 | 187 | ### ursa.coerceKey(orig) 188 | 189 | Coerce the given key value into a key object (either public or 190 | private), returning it. If given a private key object, this just 191 | returns it as-is. If given a string or Buffer, it tries to parse it as 192 | PEM. Anything else will result in an error. 193 | 194 | ### ursa.coercePrivateKey(orig) 195 | 196 | Coerce the given key value into a private key object, returning it. If 197 | given a private key object, this just returns it as-is. If given a 198 | string or Buffer, it tries to parse it as PEM. Anything else will 199 | result in an error. 200 | 201 | ### ursa.coercePublicKey(orig) 202 | 203 | Coerce the given key value into a public key object, returning it. If 204 | given a private key object, this just returns it as-is. If given a 205 | string or Buffer, it tries to parse it as PEM. Anything else will 206 | result in an error. 207 | 208 | ### ursa.createPublicKey(pem, encoding) 209 | 210 | Create and return a public key read in from the given PEM-format file. 211 | See "Public Key Methods" below for more details. 212 | 213 | ### ursa.createSigner(algorithm) 214 | 215 | Create and return a signer which can sign a hash generated with the named 216 | algorithm (such as `"sha256"` or `"md5"`). See "Signer Methods" below 217 | for more details. 218 | 219 | This function is similar to `crypto.createSign()`, except this function 220 | takes a hash algorithm name (e.g., `"sha256"`) and not a crypto+hash name 221 | combination (e.g., `"RSA-SHA256"`). 222 | 223 | ### ursa.createVerifier(algorithm) 224 | 225 | Create and return a verifier which can verify a hash generated with the 226 | named algorithm (such as `"sha256"` or `"md5"`). See "Verifier Methods" below 227 | for more details. 228 | 229 | This function is similar to `crypto.createVerify()`, except this function 230 | takes a hash algorithm name (e.g., `"sha256"`) and not a crypto+hash name 231 | combination (e.g., `"RSA-SHA256"`). 232 | 233 | ### ursa.equalKeys(key1, key2) 234 | 235 | This returns `true` if and only if both arguments are key objects of 236 | the same type (public or private) and their contents match. 237 | 238 | ### ursa.generatePrivateKey(modulusBits, exponent) 239 | 240 | Create and return a freshly-generated private key (aka a keypair). 241 | The first argument indicates the number of bits in the modulus (1024 242 | or more is generally considered secure). The second argument indicates 243 | the exponent value, which must be odd (65537 is the typical value; 3 244 | and 17 are also common). Both arguments are optional and default to 245 | 2048 and 65537 (respectively). 246 | 247 | This method will throw if `modulusBits` is less than `512` (because 248 | it's pretty crazy to want a key with that few bits) or if `exponent` 249 | is even (because RSA only works for odd exponents). 250 | 251 | Using the command-line `openssl` tool, this operation is 252 | equivalent to: 253 | 254 | ```shell 255 | openssl genrsa -out key-name.pem 256 | ``` 257 | 258 | for exponent 65537, or for exponent 3 with the additional option 259 | `-3`. (That tool doesn't support other exponents.) 260 | 261 | ### ursa.isKey(obj) 262 | 263 | Return `true` if the given object is a key object (public or private) that 264 | was created by this module. Return `false` if not. 265 | 266 | ### ursa.isPrivateKey(obj) 267 | 268 | Return `true` if the given object is a private key object that 269 | was created by this module. Return `false` if not. 270 | 271 | ### ursa.isPublicKey(obj) 272 | 273 | Return `true` if the given object is a public key object that 274 | was created by this module. Return `false` if not. 275 | 276 | Note that, even though all the public key operations work on private 277 | keys, this function only returns true if the given object is a 278 | public key, per se. 279 | 280 | ### ursa.matchingPublicKeys(key1, key2) 281 | 282 | This returns `true` if and only if both arguments are key objects of 283 | some sort (either can be public or private, and they don't have to 284 | be the same) and their public aspects match each other. 285 | 286 | ### ursa.openSshPublicKey(key, encoding) 287 | 288 | This returns `publicKey` from ssh-rsa public key-string. First argument 289 | must be a string like `ssh-rsa AAAAB3Nz.... user@localhost` or Buffer of pubKey bits. 290 | 291 | ### ursa.sshFingerprint(sshKey, sshEncoding, outEncoding) 292 | 293 | Return the SSH-style public key fingerprint of the given SSH-format 294 | public key (which was, perhaps, the result of a call to 295 | `toPublicSsh()` on a key object). 296 | 297 | This is no more and no less than an MD5 hash of the given SSH-format 298 | public key. This function doesn't actually check to see if the given 299 | key is valid (garbage in, garbage out). 300 | 301 | Using the command-line `ssh-keygen` tool, this operation is 302 | equivalent to: 303 | 304 | ```shell 305 | ssh-keygen -l -f key-name.sshpub 306 | ``` 307 | 308 | This operation is also equivalent to this: 309 | 310 | ```shell 311 | cat key-name.sshpub | awk '{print $2}' | base64 --decode | md5 312 | ``` 313 | 314 | Public Key Methods 315 | ------------------ 316 | 317 | These are all the methods available on public keys. These methods are 318 | *also* available on private keys (since private keys have all the 319 | underlying data necessary to perform the public-side operations). 320 | 321 | ### encrypt(buf, bufEncoding, outEncoding, padding) 322 | 323 | This performs the "public encrypt" operation on the given buffer. The 324 | result is always a byte sequence that is the same size as the key 325 | associated with the instance. (For example, if the key is 2048 bits, 326 | then the result of this operation will be 2048 bits, aka 256 bytes.) 327 | 328 | The input buffer is limited to be no larger than the key size 329 | minus 41 bytes. 330 | 331 | If no padding mode is specified, the default, and recommended, mode 332 | is `ursa.RSA_PKCS1_OAEP_PADDING`. The mode 333 | `ursa.RSA_PKCS1_PADDING` is also supported. 334 | 335 | ### getExponent(encoding) 336 | 337 | Get the public exponent as an unsigned big-endian byte sequence. 338 | 339 | ### getModulus(encoding) 340 | 341 | Get the public modulus as an unsigned big-endian byte sequence. 342 | 343 | ### hashAndVerify(algorithm, buf, sig, encoding, use\_pss\_padding, salt\_len) 344 | 345 | This is a friendly wrapper for verifying signatures. The given buffer 346 | is hashed using the named algorithm, and the result is verified 347 | against the given signature. This returns `true` if the hash and 348 | signature match and the signature was produced by the appropriate 349 | private key. This returns `false` if the signature is a valid signature 350 | (structurally) but doesn't match. This throws an exception in other 351 | cases. 352 | 353 | The encoding, if specified, applies to both buffer-like arguments. The 354 | algorithm must always be a string. 355 | 356 | If `use_pss_padding` is truthy then [RSASSA-PSS](http://tools.ietf.org/html/rfc3447#section-8.1) 357 | padding is used when verifying the signature. `salt_len`, if specified, is 358 | the length of the PSS salt (in bytes) or one of the following: 359 | 360 | - `RSA_PKCS1_SALT_LEN_HLEN` (the same as the hash length, default). 361 | - `RSA_PKCS1_SALT_LEN_MAX` (maximum permissable value). 362 | 363 | ### publicDecrypt(buf, bufEncoding, outEncoding) 364 | 365 | This performs the "public decrypt" operation on the given buffer. The 366 | result is always a byte sequence that is no more than the size of the 367 | key associated with the instance. (For example, if the key is 2048 368 | bits, then the result of this operation will be no more than 2048 369 | bits, aka 256 bytes.) 370 | 371 | If no padding mode is specified, the default, and recommended, mode 372 | is `ursa.RSA_PKCS1_PADDING`. The mode `ursa.RSA_NO_PADDING` is also supported. 373 | 374 | ### toPublicPem(encoding) 375 | 376 | This converts the public key data into a PEM-format file. 377 | 378 | ### toPublicSsh(encoding) 379 | 380 | This converts the public key data into an SSH-format file. This is the 381 | file format one finds in SSH's `authorized_keys` and `known_hosts` files. 382 | When used in such files, the contents are base64-encoded and prefixed with 383 | the label `ssh-rsa`. Depending on context, the line a key appears on may 384 | also have a host name prefix (in `known_hosts`) or comment suffix 385 | (in `authorized_keys`). 386 | 387 | Using the command-line `ssh-keygen` tool, this operation is equivalent to: 388 | 389 | ```shell 390 | ssh-keygen -y -f key-name.pem > key-name.sshpub 391 | ``` 392 | 393 | ### toPublicSshFingerprint(encoding) 394 | 395 | Return the SSH-style public key fingerprint of this key. See 396 | `ursa.sshFingerprint()`, above, for more details. 397 | 398 | ### verify(algorithm, hash, sig, encoding) 399 | 400 | This performs an RSA public-verify on the given hash buffer, which 401 | should be the result of performing the hash operation named by 402 | the algorithm (such as `"sha256"` or `"md5"`) on some data. The 403 | signature buffer is checked to see if it contains a private-signed 404 | statement of the algorithm and hash. The method returns `true` if 405 | the signature and hash match, or `false` if the signature and hash 406 | don't match but the signature is at least a valid signature of 407 | some sort. In any other situation, this throws an exception. 408 | 409 | The encoding, if specified, applies to both buffer-like arguments. The 410 | algorithm must always be a string. 411 | 412 | This method is the underlying one used as part of the implementation 413 | of the higher-level and much friendlier `ursa.createVerifier()` and 414 | `hashAndVerify()`. 415 | 416 | ### ununseal(ununsealer) 417 | 418 | This is an internal method that is used in the implementation of 419 | `ursa.isKey()` `ursa.isPrivateKey()` `ursa.isPublicKey()` and 420 | associated assertion functions. When called externally, it will 421 | always return `undefined`. 422 | 423 | Private Key Methods 424 | ------------------- 425 | 426 | These are the methods available on private keys, above and beyond 427 | what is available for public keys. 428 | 429 | ### decrypt(buf, bufEncoding, outEncoding, padding) 430 | 431 | This performs the "private decrypt" operation on the given buffer. The 432 | result is always a byte sequence that is no more than the size of the 433 | key associated with the instance. (For example, if the key is 2048 434 | bits, then the result of this operation will be no more than 2048 435 | bits, aka 256 bytes.) 436 | 437 | If no padding mode is specified, the default, and recommended, mode 438 | is `ursa.RSA_PKCS1_OAEP_PADDING`. The mode 439 | `ursa.RSA_PKCS1_PADDING` is also supported. 440 | 441 | ### getPrivateExponent(encoding) 442 | 443 | Get the private exponent as an unsigned big-endian byte sequence. The returned 444 | exponent is not encrypted in any way, so this method should be used with caution. 445 | 446 | ### hashAndSign(algorithm, buf, bufEncoding, outEncoding, use\_pss\_padding, salt\_len) 447 | 448 | This is a friendly wrapper for producing signatures. The given buffer 449 | is hashed using the named algorithm, and the result is signed using 450 | the private key held by this instance. The return value of this method 451 | is the signature. 452 | 453 | If `use_pss_padding` is truthy then [RSASSA-PSS](http://tools.ietf.org/html/rfc3447#section-8.1) 454 | padding is used when generating the signature. The `salt_len`, if specified, is 455 | the length of the PSS salt (in bytes) or one of the following: 456 | 457 | - `RSA_PKCS1_SALT_LEN_HLEN` (the same as the hash length, default). 458 | - `RSA_PKCS1_SALT_LEN_RECOVER` (assume `RSA_PKCS1_SALT_LEN_MAX` was used when the padding was added). 459 | 460 | ### privateEncrypt(buf, bufEncoding, outEncoding) 461 | 462 | This performs the "private encrypt" operation on the given buffer. The 463 | result is always a byte sequence that is the same size as the key 464 | associated with the instance. (For example, if the key is 2048 bits, 465 | then the result of this operation will be 2048 bits, aka 256 bytes.) 466 | 467 | The input buffer is limited to be no larger than the key size 468 | minus 12 bytes. 469 | 470 | If no padding mode is specified, the default, and recommended, mode 471 | is `ursa.RSA_PKCS1_PADDING`. The mode `ursa.RSA_NO_PADDING` is also supported. 472 | 473 | ### sign(algorithm, hash, hashEncoding, outEncoding) 474 | 475 | This performs an RSA private-sign on the given hash buffer, which 476 | should be the result of performing the hash operation named by 477 | the algorithm (such as `"sha256"` or `"md5"`) on some data. The 478 | result of this operation may later be passed to `verify()` on the 479 | corresponding public key. 480 | 481 | This method is the underlying one used as part of the implementation 482 | of the higher-level and much friendlier `ursa.createSigner()` and 483 | `hashAndSign()`. 484 | 485 | ### toPrivatePem(encoding) 486 | 487 | This converts the private key data into a PEM-format file. The result 488 | is not encrypted, so it behooves the user of this method to take care 489 | with the result if the key is sensitive from a security standpoint, 490 | which is often the case with such things. (YMMV of course.) 491 | 492 | 493 | Signer Methods 494 | -------------- 495 | 496 | These are the methods available on signer objects, which are returned 497 | by `ursa.createSigner()`. These are similar to the objects returned 498 | from `crypto.createSign()`. 499 | 500 | ### update(buf, bufEncoding) 501 | 502 | Update the hash in-progress with the given data. 503 | 504 | ### sign(privateKey, outEncoding) 505 | 506 | Get the final hash of the data, and sign it using the private key. The 507 | return value is the signature, suitable for later verification. 508 | 509 | 510 | Verifier Methods 511 | ---------------- 512 | 513 | These are the methods available on verifier objects, which are returned 514 | by `ursa.createVerifier()`. These are similar to the objects returned 515 | from `crypto.createVerify()`. 516 | 517 | ### update(buf, bufEncoding) 518 | 519 | Update the hash in-progress with the given data. 520 | 521 | ### verify(publicKey, sig, sigEncoding) 522 | 523 | Get the final hash of the data, and verify that the given signature 524 | both matches it and was produced by the private key corresponding to 525 | the given public key. 526 | 527 | This returns `true` if the signature and hash match appropriately, 528 | or `false` if the signature appears to be generally valid (e.g. 529 | structurally) yet doesn't match. This throws an exception in all 530 | other cases. 531 | 532 | 533 | Constants 534 | --------- 535 | 536 | Allowed padding modes for public/private encryption/decryption: 537 | 538 | * `ursa.RSA_PKCS1_PADDING` 539 | * `ursa.RSA_NO_PADDING` 540 | * `ursa.RSA_PKCS1_OAEP_PADDING` 541 | 542 | 543 | Contributing 544 | ------------ 545 | 546 | Questions, comments, bug reports, and pull requests are all welcome. 547 | Submit them at [the project on GitHub](https://github.com/quartzjer/ursa/). 548 | 549 | Bug reports that include steps-to-reproduce (including code) are the 550 | best. Even better, make them in the form of pull requests that update 551 | the test suite. Thanks! 552 | 553 | 554 | Authors 555 | ------- 556 | 557 | Current (2015+) maintenance by [Jeremie Miller](https://github.com/quartzjer). 558 | 559 | Previous (2014) maintenance sponsored by [NodePrime](http://nodeprime.com). 560 | 561 | Original Author (2012): [Dan Bornstein](https://github.com/danfuzz) 562 | ([personal website](http://www.milk.com/)), supported by 563 | [The Obvious Corporation](http://obvious.com/) (now [Medium](https://medium.com/)). 564 | 565 | With contribution from: 566 | 567 | * [C J Silverio](https://github.com/ceejbot) 568 | * [Tyler Neylon](https://github.com/tylerneylon) 569 | 570 | With thanks to: 571 | 572 | * [node-rsa](https://github.com/chrisa/node-rsa) by Chris Andrews, 573 | for inspiration 574 | 575 | 576 | License 577 | ------- 578 | 579 | Updates Copyright 2014 [NodePrime, Inc.](http://nodeprime.com/). 580 | Original Copyright 2012 [The Obvious Corporation](http://obvious.com/). 581 | 582 | Licensed under the Apache License, Version 2.0. 583 | See the top-level file `[LICENSE.txt](LICENSE.txt)` and 584 | (http://www.apache.org/licenses/LICENSE-2.0). 585 | 586 | Other Repos that may be of Interest: 587 | ----------------------- 588 | 589 | * https://github.com/mal/forsake 590 | * https://github.com/rzcoder/node-rsa 591 | * https://github.com/coolaj86/nodejs-self-signed-certificate-example 592 | * https://github.com/coolaj86/node-ssl-root-cas/wiki/Painless-Self-Signed-Certificates-in-node.js 593 | * https://github.com/coolaj86/node-ssl-root-cas 594 | * https://github.com/coolaj86/nodejs-ssl-trusted-peer-example 595 | * https://github.com/coolaj86/bitcrypt 596 | -------------------------------------------------------------------------------- /lib/ursa.js: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Obvious Corporation. 2 | 3 | /* 4 | * "ursa": RSA crypto, with an emphasis on Buffer objects 5 | */ 6 | 7 | /* 8 | * Modules used 9 | */ 10 | 11 | "use strict"; 12 | 13 | // Note: This also forces OpenSSL to be initialized, which is important! 14 | var crypto = require("crypto"); 15 | 16 | var assert = require("assert"); 17 | 18 | var ursaNative = require("bindings")("ursaNative"); 19 | var RsaWrap = ursaNative.RsaWrap; 20 | var textToNid = ursaNative.textToNid; 21 | 22 | 23 | /* 24 | * Variable definitions 25 | */ 26 | 27 | /** encoding constant */ 28 | var BASE64 = "base64"; 29 | 30 | /** encoding constant */ 31 | var BINARY = "binary"; 32 | 33 | /** encoding constant */ 34 | var HEX = "hex"; 35 | 36 | /** type name */ 37 | var STRING = "string"; 38 | 39 | /** encoding constant */ 40 | var UTF8 = "utf8"; 41 | 42 | /** encoding constant */ 43 | var UTF16 = "utf16le"; 44 | 45 | /** hash algorithm constant */ 46 | var MD5 = "md5"; 47 | 48 | /** regex that matches PEM files, capturing the file type */ 49 | var PEM_REGEX = 50 | /^(-----BEGIN (.*) KEY-----\r?\n[:\s,-\/+=a-zA-Z0-9\r\n]*\r?\n-----END \2 KEY-----\r?\n)/m; 51 | 52 | /** "unsealer" key object to authenticate objects */ 53 | var theUnsealer = [ "ursa unsealer" ]; 54 | 55 | 56 | /* 57 | * Helper functions 58 | */ 59 | 60 | /** 61 | * Return true iff x is either a string or a Buffer. 62 | */ 63 | function isStringOrBuffer(x) { 64 | return (typeof x === STRING) || Buffer.isBuffer(x); 65 | } 66 | 67 | /** 68 | * Extract and identify the PEM file type represented in the given 69 | * buffer. Returns the extracted type string or undefined if the 70 | * buffer doesn't seem to be any sort of PEM format file. 71 | */ 72 | function identifyPemType(buf) { 73 | var str = encodeBuffer(buf, UTF8); 74 | var match = PEM_REGEX.exec(str); 75 | 76 | if (!match) { 77 | return undefined; 78 | } 79 | 80 | return match[2]; 81 | } 82 | 83 | /** 84 | * Return whether the given buffer or string appears (trivially) to be a 85 | * valid public key file in PEM format. 86 | */ 87 | function isPublicKeyPem(buf) { 88 | var kind = identifyPemType(buf); 89 | return (kind == "PUBLIC"); 90 | } 91 | 92 | /** 93 | * Return whether the given buffer or string appears (trivially) to be a 94 | * valid private key file in PEM format. 95 | */ 96 | function isPrivateKeyPem(buf) { 97 | var kind = identifyPemType(buf); 98 | return (kind == "RSA PRIVATE"); 99 | } 100 | 101 | /** 102 | * Return a buffer containing the encoding of the given bigint for use 103 | * as part of an SSH-style public key file. The input value must be a 104 | * buffer representing an unsigned bigint in big-endian order. 105 | */ 106 | function toSshBigint(value) { 107 | // The output is signed, so we need to add an extra 00 byte at the 108 | // head if the high-order bit is set. 109 | var prefix00 = ((value[0] & 0x80) !== 0); 110 | var length = value.length + (prefix00 ? 1 : 0); 111 | var result = new Buffer(length + 4); 112 | var offset = 0; 113 | 114 | result.writeUInt32BE(length, offset); 115 | offset += 4; 116 | 117 | if (prefix00) { 118 | result[offset] = 0; 119 | offset++; 120 | } 121 | 122 | value.copy(result, offset); 123 | return result; 124 | } 125 | 126 | /** 127 | * Create and return a buffer containing an SSH-style public key file for 128 | * the given RsaWrap object. 129 | * 130 | * For the record, an SSH-style public key file consists of three 131 | * concatenated values, each one length-prefixed: 132 | * 133 | * literal string "ssh-rsa" 134 | * exponent 135 | * modulus 136 | * 137 | * The literal string header is length-prefixed. The two numbers are 138 | * represented as signed big-int values in big-endian order, also 139 | * length-prefixed. 140 | */ 141 | function createSshPublicKey(rsa) { 142 | var e = toSshBigint(rsa.getExponent()); 143 | var m = toSshBigint(rsa.getModulus()); 144 | 145 | var header = toSshBigint(new Buffer("ssh-rsa", UTF8)); 146 | var result = new Buffer(header.length + m.length + e.length); 147 | var offset = 0; 148 | 149 | header.copy(result, offset); 150 | offset += header.length; 151 | e.copy(result, offset); 152 | offset += e.length; 153 | m.copy(result, offset); 154 | 155 | return result; 156 | } 157 | 158 | /** 159 | * Validate the given encoding name. Throws an exception if invalid. 160 | */ 161 | function validateEncoding(encoding) { 162 | switch (encoding) { 163 | case BASE64: 164 | case BINARY: 165 | case HEX: 166 | case UTF16: 167 | case UTF8: { 168 | // These are all valid. 169 | break; 170 | } 171 | default: { 172 | throw new Error("Invalid encoding: " + encoding); 173 | } 174 | } 175 | } 176 | 177 | /** 178 | * Convert a buffer into an appropriately-encoded string, or return it 179 | * unmodified if the encoding is undefined. 180 | */ 181 | function encodeBuffer(buf, encoding) { 182 | if (encoding === undefined) { 183 | return buf; 184 | } 185 | 186 | validateEncoding(encoding); 187 | return buf.toString(encoding); 188 | } 189 | 190 | /** 191 | * Return a buffer or undefined argument as-is, or convert a given 192 | * string into a buffer by using the indicated encoding. An undefined 193 | * encoding is interpreted to mean UTF8. 194 | */ 195 | function decodeString(str, encoding) { 196 | if ((str === undefined) || Buffer.isBuffer(str)) { 197 | return str; 198 | } 199 | 200 | if (encoding === undefined) { 201 | encoding = UTF8; 202 | } 203 | 204 | validateEncoding(encoding); 205 | return new Buffer(str, encoding); 206 | } 207 | /** 208 | * OpenSSH Public key to RSA 209 | * @param {String|Object} key OpenSSH Public Key 210 | * @param key encoding, default 'base64' 211 | * @returns {PublicKey} 212 | */ 213 | function openSshPublicKey(key, encoding) { 214 | if (!Buffer.isBuffer(key)) { 215 | key = key.substr(0, 3) === 'ssh' ? key.split(' ')[1] : key; 216 | key = new Buffer(key, encoding || 'base64'); 217 | } 218 | 219 | function parsePublicKey(key) { 220 | var parts = [], 221 | partsLength = 3; 222 | 223 | while(key.length) { 224 | var dLen = key.readInt32BE(0); 225 | var data = key.slice(4, dLen+4); 226 | key = key.slice(4+dLen); 227 | parts.push(data); 228 | if (!(--partsLength)) break; 229 | } 230 | 231 | return { 232 | modulus : parts[2], 233 | exponent: parts[1], 234 | type : parts[0] 235 | }; 236 | } 237 | 238 | var pubKey = parsePublicKey(key); 239 | var rsa = new RsaWrap(); 240 | 241 | if (pubKey.type != 'ssh-rsa') { 242 | throw new TypeError('Only "ssh-rsa" format supported'); 243 | } 244 | 245 | rsa.openPublicSshKey(pubKey.modulus, pubKey.exponent); 246 | 247 | return PublicKey(rsa); 248 | } 249 | 250 | /** 251 | * Public Key object. This is the externally-visible object that one gets 252 | * when constructing an instance from a public key. The constructor takes 253 | * a native RsaWrap object. 254 | */ 255 | function PublicKey(rsa) { 256 | var self; 257 | 258 | function getExponent(encoding) { 259 | return encodeBuffer(rsa.getExponent(), encoding); 260 | } 261 | 262 | function getModulus(encoding) { 263 | return encodeBuffer(rsa.getModulus(), encoding); 264 | } 265 | 266 | function toPublicPem(encoding) { 267 | return encodeBuffer(rsa.getPublicKeyPem(), encoding); 268 | } 269 | 270 | function toPublicSsh(encoding) { 271 | return encodeBuffer(createSshPublicKey(rsa), encoding); 272 | } 273 | 274 | function toPublicSshFingerprint(encoding) { 275 | return sshFingerprint(createSshPublicKey(rsa), undefined, encoding); 276 | } 277 | 278 | function encrypt(buf, bufEncoding, outEncoding, padding) { 279 | buf = decodeString(buf, bufEncoding); 280 | padding = (padding === undefined) ? 281 | ursaNative.RSA_PKCS1_OAEP_PADDING : padding; 282 | return encodeBuffer(rsa.publicEncrypt(buf, padding), outEncoding); 283 | } 284 | 285 | function publicDecrypt(buf, bufEncoding, outEncoding, padding) { 286 | buf = decodeString(buf, bufEncoding); 287 | padding = (padding === undefined) ? 288 | ursaNative.RSA_PKCS1_PADDING : padding; 289 | return encodeBuffer(rsa.publicDecrypt(buf, padding), outEncoding); 290 | } 291 | 292 | function verify(algorithm, hash, sig, encoding) { 293 | algorithm = textToNid(algorithm); 294 | hash = decodeString(hash, encoding); 295 | sig = decodeString(sig, encoding); 296 | return rsa.verify(algorithm, hash, sig); 297 | } 298 | 299 | function hashAndVerify(algorithm, buf, sig, encoding, 300 | use_pss_padding, salt_len) { 301 | if (use_pss_padding) { 302 | sig = publicDecrypt(sig, encoding, undefined, 303 | ursaNative.RSA_NO_PADDING); 304 | 305 | var hash = crypto.createHash(algorithm); 306 | hash.update(decodeString(buf, encoding)); 307 | buf = new Buffer(hash.digest(BINARY), BINARY); 308 | 309 | return rsa.verifyPSSPadding(textToNid(algorithm), buf, sig, 310 | (salt_len === undefined) ? ursaNative.RSA_PKCS1_SALT_LEN_HLEN : salt_len); 311 | } else { 312 | var verifier = createVerifier(algorithm); 313 | verifier.update(buf, encoding); 314 | return verifier.verify(self, sig, encoding); 315 | } 316 | } 317 | 318 | function unseal(unsealer) { 319 | return (unsealer === theUnsealer) ? self : undefined; 320 | } 321 | 322 | self = { 323 | encrypt: encrypt, 324 | getExponent: getExponent, 325 | getModulus: getModulus, 326 | hashAndVerify: hashAndVerify, 327 | publicDecrypt: publicDecrypt, 328 | toPublicPem: toPublicPem, 329 | toPublicSsh: toPublicSsh, 330 | toPublicSshFingerprint: toPublicSshFingerprint, 331 | verify: verify, 332 | unseal: unseal 333 | }; 334 | 335 | return self; 336 | } 337 | 338 | /** 339 | * Private Key object. This is the externally-visible object that one 340 | * gets when constructing an instance from a private key (aka a 341 | * keypair). The constructor takes a native RsaWrap object. 342 | */ 343 | function PrivateKey(rsa) { 344 | var self; 345 | 346 | function getPrivateExponent(encoding) { 347 | return encodeBuffer(rsa.getPrivateExponent(), encoding); 348 | } 349 | 350 | function toPrivatePem(encoding) { 351 | return encodeBuffer(rsa.getPrivateKeyPem(), encoding); 352 | } 353 | 354 | function toEncryptedPrivatePem(passPhrase, cipher, encoding) { 355 | if(!passPhrase) return toPrivatePem(encoding); 356 | return encodeBuffer(rsa.getPrivateKeyPem(passPhrase, cipher)); 357 | } 358 | 359 | function decrypt(buf, bufEncoding, outEncoding, padding) { 360 | buf = decodeString(buf, bufEncoding); 361 | padding = (padding === undefined) ? ursaNative.RSA_PKCS1_OAEP_PADDING : padding; 362 | return encodeBuffer(rsa.privateDecrypt(buf, padding), outEncoding); 363 | } 364 | 365 | function privateEncrypt(buf, bufEncoding, outEncoding, padding) { 366 | buf = decodeString(buf, bufEncoding); 367 | padding = (padding === undefined) ? ursaNative.RSA_PKCS1_PADDING : padding; 368 | return encodeBuffer(rsa.privateEncrypt(buf, padding), outEncoding); 369 | } 370 | 371 | function sign(algorithm, hash, hashEncoding, outEncoding) { 372 | algorithm = textToNid(algorithm); 373 | hash = decodeString(hash, hashEncoding); 374 | return encodeBuffer(rsa.sign(algorithm, hash), outEncoding); 375 | } 376 | 377 | function hashAndSign(algorithm, buf, bufEncoding, outEncoding, 378 | use_pss_padding, salt_len) { 379 | if (use_pss_padding) { 380 | var hash = crypto.createHash(algorithm); 381 | hash.update(decodeString(buf, bufEncoding)); 382 | buf = new Buffer(hash.digest(BINARY), BINARY); 383 | 384 | buf = rsa.addPSSPadding(textToNid(algorithm), buf, 385 | (salt_len === undefined) ? ursaNative.RSA_PKCS1_SALT_LEN_HLEN : salt_len); 386 | 387 | return privateEncrypt(buf, undefined, outEncoding, 388 | ursaNative.RSA_NO_PADDING); 389 | } else { 390 | var signer = createSigner(algorithm); 391 | signer.update(buf, bufEncoding); 392 | return signer.sign(self, outEncoding); 393 | } 394 | } 395 | 396 | self = PublicKey(rsa); 397 | self.decrypt = decrypt; 398 | self.getPrivateExponent = getPrivateExponent; 399 | self.hashAndSign = hashAndSign; 400 | self.privateEncrypt = privateEncrypt; 401 | self.sign = sign; 402 | self.toPrivatePem = toPrivatePem; 403 | self.toEncryptedPrivatePem = toEncryptedPrivatePem; 404 | return self; 405 | } 406 | 407 | 408 | /* 409 | * Exported bindings 410 | */ 411 | 412 | /** 413 | * Create a new public key object, from the given PEM-encoded file. 414 | */ 415 | function createPublicKey(pem, encoding) { 416 | var rsa = new RsaWrap(); 417 | pem = decodeString(pem, encoding); 418 | 419 | try { 420 | rsa.setPublicKeyPem(pem); 421 | } catch (ex) { 422 | if (!isPublicKeyPem(pem)) { 423 | throw new Error("Not a public key."); 424 | } 425 | throw ex; 426 | } 427 | 428 | return PublicKey(rsa); 429 | } 430 | 431 | /** 432 | * Create a new private key object, from the given PEM-encoded file, 433 | * optionally decrypting the file with a password. 434 | */ 435 | function createPrivateKey(pem, password, encoding) { 436 | var rsa = new RsaWrap(); 437 | pem = decodeString(pem, encoding); 438 | password = decodeString(password, encoding); 439 | 440 | try { 441 | // Note: The native code is sensitive to the actual number of 442 | // arguments. It's *not* okay to pass undefined as a password. 443 | if (password) { 444 | rsa.setPrivateKeyPem(pem, password); 445 | } else { 446 | rsa.setPrivateKeyPem(pem); 447 | } 448 | } catch (ex) { 449 | if (!isPrivateKeyPem(pem)) { 450 | throw new Error("Not a private key."); 451 | } 452 | throw ex; 453 | } 454 | 455 | return PrivateKey(rsa); 456 | } 457 | 458 | /** 459 | * Create public key from components 460 | */ 461 | function createPublicKeyFromComponents(modulus, exponent) { 462 | var rsa = new RsaWrap(); 463 | rsa.createPublicKeyFromComponents(modulus, exponent); 464 | return PublicKey(rsa); 465 | } 466 | 467 | /** 468 | * Create private key from components 469 | */ 470 | function createPrivateKeyFromComponents(modulus, exponent, p, q, dp, dq, inverseQ, d) { 471 | var rsa = new RsaWrap(); 472 | rsa.createPrivateKeyFromComponents(modulus, exponent, p, q, dp, dq, inverseQ, d); 473 | 474 | return PrivateKey(rsa); 475 | } 476 | 477 | /** 478 | * Generate a new private key object (aka a keypair). 479 | */ 480 | function generatePrivateKey(modulusBits, exponent) { 481 | if (modulusBits === undefined) { 482 | modulusBits = 2048; 483 | } 484 | 485 | if (exponent === undefined) { 486 | exponent = 65537; 487 | } 488 | 489 | var rsa = new RsaWrap(); 490 | rsa.generatePrivateKey(modulusBits, exponent); 491 | 492 | return PrivateKey(rsa); 493 | } 494 | 495 | /** 496 | * Create a key object from a PEM format file, either a private or 497 | * public key depending on what kind of file is passed in. If given 498 | * a private key file, it must not be encrypted. 499 | */ 500 | function createKey(pem, encoding) { 501 | pem = decodeString(pem, encoding); 502 | 503 | if (isPublicKeyPem(pem)) { 504 | return createPublicKey(pem); 505 | } else if (isPrivateKeyPem(pem)) { 506 | return createPrivateKey(pem); 507 | } else { 508 | throw new Error("Not a key."); 509 | } 510 | } 511 | 512 | /** 513 | * Return the SSH-style public key fingerprint of the given SSH-format 514 | * public key. 515 | */ 516 | function sshFingerprint(sshKey, sshEncoding, outEncoding) { 517 | var hash = crypto.createHash(MD5); 518 | 519 | hash.update(decodeString(sshKey, sshEncoding)); 520 | var result = new Buffer(hash.digest(BINARY), BINARY); 521 | return encodeBuffer(result, outEncoding); 522 | } 523 | 524 | /** 525 | * Return whether the given object is a key object (either public or 526 | * private), as constructed by this module. 527 | */ 528 | function isKey(obj) { 529 | var obj2; 530 | 531 | try { 532 | var unseal = obj.unseal; 533 | if (typeof unseal !== "function") { 534 | return false; 535 | } 536 | obj2 = unseal(theUnsealer); 537 | } catch (ex) { 538 | // Ignore; can't assume that other objects obey any particular 539 | // unsealing protocol. 540 | // TODO: Log? 541 | return false; 542 | } 543 | 544 | return obj2 !== undefined; 545 | } 546 | 547 | /** 548 | * Return whether the given object is a private key object, as 549 | * constructed by this module. 550 | */ 551 | function isPrivateKey(obj) { 552 | return isKey(obj) && (obj.decrypt !== undefined); 553 | } 554 | 555 | /** 556 | * Return whether the given object is a public key object (per se), as 557 | * constructed by this module. 558 | */ 559 | function isPublicKey(obj) { 560 | return isKey(obj) && !isPrivateKey(obj); 561 | } 562 | 563 | /** 564 | * Assert wrapper for isKey(). 565 | */ 566 | function assertKey(obj) { 567 | assert(isKey(obj)); 568 | } 569 | 570 | /** 571 | * Assert wrapper for isPrivateKey(). 572 | */ 573 | function assertPrivateKey(obj) { 574 | assert(isPrivateKey(obj)); 575 | } 576 | 577 | /** 578 | * Assert wrapper for isPublicKey(). 579 | */ 580 | function assertPublicKey(obj) { 581 | assert(isPublicKey(obj)); 582 | } 583 | 584 | /** 585 | * Coerce the given key value into an private key object, returning 586 | * it. If given a private key object, this just returns it as-is. If 587 | * given a string or Buffer, it tries to parse it as PEM. Anything 588 | * else is an error. 589 | */ 590 | function coercePrivateKey(orig) { 591 | if (isPrivateKey(orig)) { 592 | return orig; 593 | } else if (isStringOrBuffer(orig)) { 594 | return createPrivateKey(orig); 595 | } 596 | 597 | throw new Error("Not a private key: " + orig); 598 | } 599 | 600 | /** 601 | * Coerce the given key value into a public key object, returning 602 | * it. If given a private key object, this just returns it as-is. If 603 | * given a string or Buffer, it tries to parse it as PEM. Anything 604 | * else is an error. 605 | */ 606 | function coercePublicKey(orig) { 607 | if (isPublicKey(orig)) { 608 | return orig; 609 | } else if (isStringOrBuffer(orig)) { 610 | return createPublicKey(orig); 611 | } 612 | 613 | throw new Error("Not a public key: " + orig); 614 | } 615 | 616 | /** 617 | * Coerce the given key value into a key object (either public or 618 | * private), returning it. If given a private key object, this just 619 | * returns it as-is. If given a string or Buffer, it tries to parse it 620 | * as PEM. Anything else is an error. 621 | */ 622 | function coerceKey(orig) { 623 | if (isKey(orig)) { 624 | return orig; 625 | } else if (isStringOrBuffer(orig)) { 626 | return createKey(orig); 627 | } 628 | 629 | throw new Error("Not a key: " + orig); 630 | } 631 | 632 | /** 633 | * Check whether the two objects are both keys of some sort and 634 | * have the same public part. 635 | */ 636 | function matchingPublicKeys(key1, key2) { 637 | if (!(isKey(key1) && isKey(key2))) { 638 | return false; 639 | } 640 | 641 | // This isn't the most efficient implementation, but it will suffice: 642 | // We convert both to ssh form, which has very little leeway for 643 | // variation, and compare bytes. 644 | 645 | var ssh1 = key1.toPublicSsh(UTF8); 646 | var ssh2 = key2.toPublicSsh(UTF8); 647 | 648 | return ssh1 === ssh2; 649 | } 650 | 651 | /** 652 | * Check whether the two objects are both keys of some sort, are 653 | * both public or both private, and have the same contents. 654 | */ 655 | function equalKeys(key1, key2) { 656 | // See above for rationale. In this case, there's no ssh form for 657 | // private keys, so we just use PEM for that. 658 | 659 | if (isPrivateKey(key1) && isPrivateKey(key2)) { 660 | var pem1 = key1.toPrivatePem(UTF8); 661 | var pem2 = key2.toPrivatePem(UTF8); 662 | return pem1 === pem2; 663 | } 664 | 665 | if (isPublicKey(key1) && isPublicKey(key2)) { 666 | return matchingPublicKeys(key1, key2); 667 | } 668 | 669 | return false; 670 | } 671 | 672 | /** 673 | * Create a signer object. 674 | */ 675 | function createSigner(algorithm) { 676 | var hash = crypto.createHash(algorithm); 677 | var self = {}; 678 | 679 | function update(buf, bufEncoding) { 680 | buf = decodeString(buf, bufEncoding); 681 | hash.update(buf); 682 | return self; 683 | } 684 | 685 | function sign(privateKey, outEncoding) { 686 | var hashBuf = new Buffer(hash.digest(BINARY), BINARY); 687 | return privateKey.sign(algorithm, hashBuf, undefined, outEncoding); 688 | } 689 | 690 | self.sign = sign; 691 | self.update = update; 692 | return self; 693 | } 694 | 695 | /** 696 | * Create a verifier object. 697 | */ 698 | function createVerifier(algorithm) { 699 | var hash = crypto.createHash(algorithm); 700 | var self = {}; 701 | 702 | function update(buf, bufEncoding) { 703 | buf = decodeString(buf, bufEncoding); 704 | hash.update(buf); 705 | return self; 706 | } 707 | 708 | function verify(publicKey, sig, sigEncoding) { 709 | var hashBuf = new Buffer(hash.digest(BINARY), BINARY); 710 | sig = decodeString(sig, sigEncoding); 711 | return publicKey.verify(algorithm, hashBuf, sig); 712 | } 713 | 714 | self.update = update; 715 | self.verify = verify; 716 | return self; 717 | } 718 | 719 | 720 | /* 721 | * Initialization 722 | */ 723 | 724 | module.exports = { 725 | assertKey: assertKey, 726 | assertPrivateKey: assertPrivateKey, 727 | assertPublicKey: assertPublicKey, 728 | coerceKey: coerceKey, 729 | coercePrivateKey: coercePrivateKey, 730 | coercePublicKey: coercePublicKey, 731 | createKey: createKey, 732 | createPrivateKey: createPrivateKey, 733 | createPrivateKeyFromComponents: createPrivateKeyFromComponents, 734 | openSshPublicKey: openSshPublicKey, 735 | createPublicKey: createPublicKey, 736 | createPublicKeyFromComponents: createPublicKeyFromComponents, 737 | createSigner: createSigner, 738 | createVerifier: createVerifier, 739 | equalKeys: equalKeys, 740 | generatePrivateKey: generatePrivateKey, 741 | isKey: isKey, 742 | isPrivateKey: isPrivateKey, 743 | isPublicKey: isPublicKey, 744 | matchingPublicKeys: matchingPublicKeys, 745 | sshFingerprint: sshFingerprint, 746 | RSA_NO_PADDING: ursaNative.RSA_NO_PADDING, 747 | RSA_PKCS1_PADDING: ursaNative.RSA_PKCS1_PADDING, 748 | RSA_PKCS1_OAEP_PADDING: ursaNative.RSA_PKCS1_OAEP_PADDING, 749 | RSA_PKCS1_SALT_LEN_HLEN: ursaNative.RSA_PKCS1_SALT_LEN_HLEN, 750 | RSA_PKCS1_SALT_LEN_MAX: ursaNative.RSA_PKCS1_SALT_LEN_MAX, 751 | RSA_PKCS1_SALT_LEN_RECOVER: ursaNative.RSA_PKCS1_SALT_LEN_RECOVER 752 | }; 753 | -------------------------------------------------------------------------------- /test/native.js: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Obvious Corporation. 2 | 3 | /* 4 | * Tests of the underlying RsaWrap class. 5 | */ 6 | 7 | /* 8 | * Modules used 9 | */ 10 | 11 | "use strict"; 12 | 13 | var assert = require("assert"); 14 | 15 | var fixture = require("./fixture"); 16 | var RsaWrap = fixture.RsaWrap; 17 | var ursaNative = fixture.ursaNative; 18 | var textToNid = ursaNative.textToNid; 19 | var nodeVersion = Number(process.version.match(/^v(\d+\.\d+)/)[1]); 20 | 21 | /** 22 | * Asserts that two strings are equal, ignoring Windows newline differences 23 | */ 24 | function assertStringEqual(actual, expected, message) { 25 | assert.equal(actual.replace(/\r\n/g, '\n'), expected.replace(/\r\n/g, '\n'), message); 26 | } 27 | 28 | describe('native', function() { 29 | it('new', function() { 30 | new RsaWrap(); 31 | }); 32 | 33 | it('setPrivateKeyPem', function() { 34 | var rsa = new RsaWrap(); 35 | rsa.setPrivateKeyPem(fixture.PRIVATE_KEY); 36 | 37 | rsa = new RsaWrap(); 38 | rsa.setPrivateKeyPem(fixture.PASS_PRIVATE_KEY, fixture.PASSWORD); 39 | }); 40 | 41 | it('fail_setPrivateKeyPem', function() { 42 | var rsa = new RsaWrap(); 43 | 44 | function f1() { 45 | rsa.setPrivateKeyPem(); 46 | } 47 | assert.throws(f1, /Missing args\[0]\./); 48 | 49 | function f2() { 50 | rsa.setPrivateKeyPem("x"); 51 | } 52 | assert.throws(f2, /Expected a Buffer in args\[0]\./); 53 | 54 | function f3() { 55 | rsa.setPrivateKeyPem(new Buffer("x")); 56 | } 57 | assert.throws(f3, /no start line/); 58 | 59 | function f4() { 60 | rsa.setPrivateKeyPem(fixture.PASS_PRIVATE_KEY, undefined); 61 | } 62 | assert.throws(f4, /Expected a Buffer in args\[1]\./); 63 | 64 | function f5() { 65 | rsa.setPrivateKeyPem(fixture.PASS_PRIVATE_KEY, "x"); 66 | } 67 | assert.throws(f5, /Expected a Buffer in args\[1]\./); 68 | 69 | function f6() { 70 | rsa.setPrivateKeyPem(fixture.PASS_PRIVATE_KEY, 71 | new Buffer("INCORRECT PASS")); 72 | } 73 | assert.throws(f6, /bad decrypt/); 74 | 75 | // Check for "set once." 76 | function f7() { 77 | rsa.setPrivateKeyPem(fixture.PRIVATE_KEY); 78 | } 79 | f7(); 80 | assert.throws(f7, /Key already set\./); 81 | }); 82 | 83 | it('setPublicKeyPem', function() { 84 | var rsa = new RsaWrap(); 85 | rsa.setPublicKeyPem(fixture.PUBLIC_KEY); 86 | }); 87 | 88 | it('fail_setPublicKeyPem', function() { 89 | var rsa = new RsaWrap(); 90 | 91 | function f1() { 92 | rsa.setPublicKeyPem(); 93 | } 94 | assert.throws(f1, /Missing args\[0]\./); 95 | 96 | function f2() { 97 | rsa.setPublicKeyPem("x"); 98 | } 99 | assert.throws(f2, /Expected a Buffer in args\[0]\./); 100 | 101 | function f3() { 102 | rsa.setPublicKeyPem(new Buffer("x")); 103 | } 104 | assert.throws(f3, /no start line/); 105 | 106 | // Check for "set once." 107 | function f4() { 108 | rsa.setPublicKeyPem(fixture.PUBLIC_KEY); 109 | } 110 | f4(); 111 | assert.throws(f4, /Key already set\./); 112 | }); 113 | 114 | it('getExponent', function() { 115 | var rsa = new RsaWrap(); 116 | rsa.setPublicKeyPem(fixture.PUBLIC_KEY); 117 | var value = rsa.getExponent().toString(fixture.HEX); 118 | assert.equal(value, fixture.EXPONENT_HEX); 119 | 120 | rsa = new RsaWrap(); 121 | rsa.setPrivateKeyPem(fixture.PRIVATE_KEY); 122 | value = rsa.getExponent().toString(fixture.HEX); 123 | assert.equal(value, fixture.EXPONENT_HEX); 124 | }); 125 | 126 | it('fail_getExponent', function() { 127 | var rsa = new RsaWrap(); 128 | 129 | function f1() { 130 | rsa.getExponent(); 131 | } 132 | assert.throws(f1, /Key not yet set\./); 133 | }); 134 | 135 | it('getModulus', function() { 136 | var rsa = new RsaWrap(); 137 | rsa.setPublicKeyPem(fixture.PUBLIC_KEY); 138 | var value = rsa.getModulus().toString(fixture.HEX); 139 | assert.equal(value, fixture.MODULUS_HEX); 140 | 141 | rsa = new RsaWrap(); 142 | rsa.setPrivateKeyPem(fixture.PRIVATE_KEY); 143 | value = rsa.getModulus().toString(fixture.HEX); 144 | assert.equal(value, fixture.MODULUS_HEX); 145 | }); 146 | 147 | it('fail_getModulus', function() { 148 | var rsa = new RsaWrap(); 149 | 150 | function f1() { 151 | rsa.getModulus(); 152 | } 153 | assert.throws(f1, /Key not yet set\./); 154 | }); 155 | 156 | it('getPrivateExponent', function() { 157 | var rsa = new RsaWrap(); 158 | rsa.createPrivateKeyFromComponents( 159 | fixture.PRIVATE_KEY_COMPONENTS.modulus, 160 | fixture.PRIVATE_KEY_COMPONENTS.exponent, 161 | fixture.PRIVATE_KEY_COMPONENTS.p, 162 | fixture.PRIVATE_KEY_COMPONENTS.q, 163 | fixture.PRIVATE_KEY_COMPONENTS.dp, 164 | fixture.PRIVATE_KEY_COMPONENTS.dq, 165 | fixture.PRIVATE_KEY_COMPONENTS.inverseQ, 166 | fixture.PRIVATE_KEY_COMPONENTS.d); 167 | 168 | var value = rsa.getPrivateExponent(); 169 | assert.equal(value.toString(fixture.HEX), fixture.PRIVATE_KEY_COMPONENTS.d.toString(fixture.HEX)); 170 | }); 171 | 172 | it('getPrivateKeyPem', function() { 173 | var keyStr = fixture.PRIVATE_KEY.toString(fixture.UTF8); 174 | 175 | var rsa = new RsaWrap(); 176 | rsa.setPrivateKeyPem(fixture.PRIVATE_KEY); 177 | 178 | var pem = rsa.getPrivateKeyPem().toString(fixture.UTF8); 179 | assertStringEqual(pem, keyStr); 180 | }); 181 | 182 | it.skip('getPrivateKeyPemWithPassPhrase', function() { 183 | var keyStr = fixture.PASS_PRIVATE_KEY.toString(fixture.UTF8); 184 | 185 | var rsa = new RsaWrap(); 186 | rsa.setPrivateKeyPem(fixture.PASS_PRIVATE_KEY, fixture.PASSWORD); 187 | 188 | var pem = rsa.getPrivateKeyPem(fixture.PASSWORD, fixture.DES_EDE3_CBC).toString(fixture.UTF8); 189 | assertStringEqual(pem, keyStr); 190 | }) 191 | 192 | it('fail_getPrivateKeyPem', function() { 193 | var rsa = new RsaWrap(); 194 | 195 | function f1() { 196 | rsa.getPrivateKeyPem(); 197 | } 198 | 199 | assert.throws(f1, /Key not yet set\./); 200 | rsa.setPublicKeyPem(fixture.PUBLIC_KEY); 201 | assert.throws(f1, /Expected a private key\./); 202 | }); 203 | 204 | it('getPublicKeyPem', function() { 205 | var keyStr = fixture.PUBLIC_KEY.toString(fixture.UTF8); 206 | 207 | var rsa = new RsaWrap(); 208 | rsa.setPublicKeyPem(fixture.PUBLIC_KEY); 209 | var pem = rsa.getPublicKeyPem().toString(fixture.UTF8); 210 | assertStringEqual(pem, keyStr); 211 | 212 | rsa = new RsaWrap(); 213 | rsa.setPrivateKeyPem(fixture.PRIVATE_KEY); 214 | pem = rsa.getPublicKeyPem().toString(fixture.UTF8); 215 | assertStringEqual(pem, keyStr); 216 | }); 217 | 218 | it('fail_getPublicKeyPem', function() { 219 | var rsa = new RsaWrap(); 220 | 221 | function f1() { 222 | rsa.getPublicKeyPem(); 223 | } 224 | assert.throws(f1, /Key not yet set\./); 225 | }); 226 | 227 | it('privateDecrypt', function() { 228 | var rsa = new RsaWrap(); 229 | rsa.setPrivateKeyPem(fixture.PRIVATE_KEY); 230 | 231 | var encoded = new Buffer(fixture.PRIVATE_CIPHERTEXT_HEX, fixture.HEX); 232 | var decoded = rsa.privateDecrypt(encoded, ursaNative.RSA_PKCS1_OAEP_PADDING).toString(fixture.UTF8); 233 | assert.equal(decoded, fixture.PLAINTEXT); 234 | 235 | encoded = new Buffer(fixture.PRIVATE_OLD_PAD_CIPHER_HEX, fixture.HEX); 236 | decoded = rsa.privateDecrypt(encoded, ursaNative.RSA_PKCS1_PADDING).toString(fixture.UTF8); 237 | assert.equal(decoded, fixture.PLAINTEXT); 238 | }); 239 | 240 | it('fail_privateDecrypt', function() { 241 | var rsa = new RsaWrap(); 242 | 243 | function f1() { 244 | rsa.privateDecrypt(); 245 | } 246 | 247 | assert.throws(f1, /Key not yet set\./); 248 | rsa.setPublicKeyPem(fixture.PUBLIC_KEY); 249 | assert.throws(f1, /Expected a private key\./); 250 | 251 | rsa = new RsaWrap(); 252 | rsa.setPrivateKeyPem(fixture.PRIVATE_KEY); 253 | 254 | function f2() { 255 | rsa.privateDecrypt("x", ursaNative.RSA_PKCS1_OAEP_PADDING); 256 | } 257 | assert.throws(f2, /Expected a Buffer in args\[0]\./); 258 | 259 | function f3() { 260 | rsa.privateDecrypt(new Buffer("x"), ursaNative.RSA_PKCS1_OAEP_PADDING); 261 | } 262 | assert.throws(f3, /decoding error/); 263 | 264 | function f4() { 265 | rsa.privateDecrypt(new Buffer("x"), "str"); 266 | } 267 | assert.throws(f4, /Expected a 32-bit integer/); 268 | }); 269 | 270 | it('publicEncrypt', function() { 271 | // No other reasonable way to test this than to do a round trip. 272 | var plainBuf = new Buffer(fixture.PLAINTEXT, fixture.UTF8); 273 | var priv = new RsaWrap(); 274 | priv.setPrivateKeyPem(fixture.PRIVATE_KEY); 275 | 276 | var rsa = new RsaWrap(); 277 | rsa.setPublicKeyPem(fixture.PUBLIC_KEY); 278 | var encoded = rsa.publicEncrypt(plainBuf, ursaNative.RSA_PKCS1_OAEP_PADDING); 279 | var decoded = priv.privateDecrypt(encoded, ursaNative.RSA_PKCS1_OAEP_PADDING).toString(fixture.UTF8); 280 | assert.equal(decoded, fixture.PLAINTEXT); 281 | 282 | encoded = priv.publicEncrypt(plainBuf, ursaNative.RSA_PKCS1_OAEP_PADDING); 283 | decoded = priv.privateDecrypt(encoded, ursaNative.RSA_PKCS1_OAEP_PADDING).toString(fixture.UTF8); 284 | assert.equal(decoded, fixture.PLAINTEXT); 285 | 286 | // Test with old-style padding. 287 | encoded = rsa.publicEncrypt(plainBuf, ursaNative.RSA_PKCS1_PADDING); 288 | decoded = priv.privateDecrypt(encoded, ursaNative.RSA_PKCS1_PADDING); 289 | decoded = decoded.toString(fixture.UTF8); 290 | assert.equal(decoded, fixture.PLAINTEXT); 291 | }); 292 | 293 | it('fail_publicEncrypt', function() { 294 | var rsa = new RsaWrap(); 295 | 296 | function f1() { 297 | rsa.publicEncrypt(); 298 | } 299 | 300 | assert.throws(f1, /Key not yet set\./); 301 | 302 | rsa = new RsaWrap(); 303 | rsa.setPublicKeyPem(fixture.PUBLIC_KEY); 304 | 305 | function f2() { 306 | rsa.publicEncrypt("x", ursaNative.RSA_PKCS1_OAEP_PADDING); 307 | } 308 | assert.throws(f2, /Expected a Buffer in args\[0]\./); 309 | 310 | function f3() { 311 | rsa.publicEncrypt(new Buffer(2048), ursaNative.RSA_PKCS1_OAEP_PADDING); 312 | } 313 | assert.throws(f3, /too large/); 314 | 315 | function f4() { 316 | rsa.publicEncrypt(new Buffer("x"), "str"); 317 | } 318 | assert.throws(f4, /Expected a 32-bit integer/); 319 | }); 320 | 321 | it('privateEncrypt', function() { 322 | var rsa = new RsaWrap(); 323 | rsa.setPrivateKeyPem(fixture.PRIVATE_KEY); 324 | 325 | var plainBuf = new Buffer(fixture.PLAINTEXT, fixture.UTF8); 326 | var encoded = rsa.privateEncrypt(plainBuf, ursaNative.RSA_PKCS1_PADDING).toString(fixture.HEX); 327 | 328 | assert.equal(encoded, fixture.PUBLIC_CIPHERTEXT_HEX); 329 | }); 330 | 331 | it('fail_privateEncrypt', function() { 332 | var rsa = new RsaWrap(); 333 | 334 | function f1() { 335 | rsa.privateEncrypt(); 336 | } 337 | 338 | assert.throws(f1, /Key not yet set\./); 339 | 340 | rsa = new RsaWrap(); 341 | rsa.setPublicKeyPem(fixture.PUBLIC_KEY); 342 | assert.throws(f1, /Expected a private key\./); 343 | 344 | rsa = new RsaWrap(); 345 | rsa.setPrivateKeyPem(fixture.PRIVATE_KEY); 346 | 347 | function f2() { 348 | rsa.privateEncrypt("x", ursaNative.RSA_PKCS1_PADDING); 349 | } 350 | assert.throws(f2, /Expected a Buffer in args\[0]\./); 351 | 352 | function f3() { 353 | rsa.privateEncrypt(new Buffer(2048), ursaNative.RSA_PKCS1_PADDING); 354 | } 355 | assert.throws(f3, /too large/); 356 | }); 357 | 358 | it('publicDecrypt', function() { 359 | var rsa = new RsaWrap(); 360 | rsa.setPublicKeyPem(fixture.PUBLIC_KEY); 361 | var encoded = new Buffer(fixture.PUBLIC_CIPHERTEXT_HEX, fixture.HEX); 362 | var decoded = rsa.publicDecrypt(encoded, ursaNative.RSA_PKCS1_PADDING).toString(fixture.UTF8); 363 | assert.equal(decoded, fixture.PLAINTEXT); 364 | 365 | rsa = new RsaWrap(); 366 | rsa.setPrivateKeyPem(fixture.PRIVATE_KEY); 367 | encoded = new Buffer(fixture.PUBLIC_CIPHERTEXT_HEX, fixture.HEX); 368 | decoded = rsa.publicDecrypt(encoded, ursaNative.RSA_PKCS1_PADDING).toString(fixture.UTF8); 369 | assert.equal(decoded, fixture.PLAINTEXT); 370 | }); 371 | 372 | it('fail_publicDecrypt', function() { 373 | var rsa = new RsaWrap(); 374 | 375 | function f1() { 376 | rsa.publicDecrypt(); 377 | } 378 | 379 | assert.throws(f1, /Key not yet set\./); 380 | rsa.setPublicKeyPem(fixture.PUBLIC_KEY); 381 | 382 | function f2() { 383 | rsa.publicDecrypt("x", ursaNative.RSA_PKCS1_PADDING); 384 | } 385 | assert.throws(f2, /Expected a Buffer in args\[0]\./); 386 | 387 | function f3() { 388 | rsa.publicDecrypt(new Buffer("x"), ursaNative.RSA_PKCS1_PADDING); 389 | } 390 | assert.throws(f3, /padding_check/); 391 | }); 392 | 393 | it('generatePrivateKey', function() { 394 | var rsa = new RsaWrap(); 395 | rsa.generatePrivateKey(512, 65537); 396 | 397 | // Do a round trip check. 398 | var plainBuf = new Buffer(fixture.PLAINTEXT, fixture.UTF8); 399 | var encoded = rsa.publicEncrypt(plainBuf, ursaNative.RSA_PKCS1_OAEP_PADDING); 400 | var decoded = rsa.privateDecrypt(encoded, ursaNative.RSA_PKCS1_OAEP_PADDING).toString(fixture.UTF8); 401 | assert.equal(decoded, fixture.PLAINTEXT); 402 | 403 | // Extract the public key, and try using it for a round trip. 404 | var pubKey = new RsaWrap(); 405 | pubKey.setPublicKeyPem(rsa.getPublicKeyPem()); 406 | encoded = pubKey.publicEncrypt(plainBuf, ursaNative.RSA_PKCS1_OAEP_PADDING); 407 | decoded = rsa.privateDecrypt(encoded, ursaNative.RSA_PKCS1_OAEP_PADDING).toString(fixture.UTF8); 408 | assert.equal(decoded, fixture.PLAINTEXT); 409 | 410 | // Similarly, try decoding with an extracted private key. 411 | var privKey = new RsaWrap(); 412 | privKey.setPrivateKeyPem(rsa.getPrivateKeyPem()); 413 | decoded = privKey.privateDecrypt(encoded, ursaNative.RSA_PKCS1_OAEP_PADDING).toString(fixture.UTF8); 414 | assert.equal(decoded, fixture.PLAINTEXT); 415 | }); 416 | 417 | it('fail_generatePrivateKey', function() { 418 | var rsa = new RsaWrap(); 419 | 420 | function f1() { 421 | rsa.generatePrivateKey(); 422 | } 423 | assert.throws(f1, /Missing args\[0]\./); 424 | 425 | function f2() { 426 | rsa.generatePrivateKey("x"); 427 | } 428 | assert.throws(f2, /Expected a 32-bit integer in args\[0]\./); 429 | 430 | function f3() { 431 | rsa.generatePrivateKey(10); 432 | } 433 | assert.throws(f3, /Missing args\[1]\./); 434 | 435 | function f4() { 436 | rsa.generatePrivateKey(20, "x"); 437 | } 438 | assert.throws(f4, /Expected a 32-bit integer in args\[1]\./); 439 | 440 | function f5() { 441 | rsa.generatePrivateKey(512, 2); 442 | } 443 | assert.throws(f5, /Expected odd exponent\./); 444 | 445 | function f6() { 446 | rsa.generatePrivateKey(512, 0); 447 | } 448 | assert.throws(f6, /Expected positive exponent\./); 449 | 450 | function f7() { 451 | rsa.generatePrivateKey(511, 1); 452 | } 453 | assert.throws(f7, /Expected modulus bit count >= 512\./); 454 | 455 | // Use the original f1(), above, for this test. 456 | rsa.setPublicKeyPem(fixture.PUBLIC_KEY); 457 | assert.throws(f1, /Key already set\./); 458 | }); 459 | 460 | it('sign', function() { 461 | var rsa = new RsaWrap(); 462 | rsa.setPrivateKeyPem(fixture.PRIVATE_KEY); 463 | 464 | var buf = new Buffer(fixture.FAKE_SHA256_TO_SIGN, fixture.HEX); 465 | var sig = rsa.sign(textToNid(fixture.SHA256), buf); 466 | 467 | assert.equal(sig.toString(fixture.HEX), fixture.FAKE_SHA256_SIGNATURE); 468 | 469 | buf = new Buffer(fixture.PLAINTEXT_SHA256, fixture.HEX); 470 | sig = rsa.sign(textToNid(fixture.SHA256), buf); 471 | 472 | assert.equal(sig.toString(fixture.HEX), fixture.PLAINTEXT_SHA256_SIGNATURE); 473 | }); 474 | 475 | it('fail_sign', function() { 476 | var rsa = new RsaWrap(); 477 | 478 | function f1() { 479 | rsa.sign(); 480 | } 481 | 482 | assert.throws(f1, /Key not yet set\./); 483 | 484 | rsa = new RsaWrap(); 485 | rsa.setPublicKeyPem(fixture.PUBLIC_KEY); 486 | assert.throws(f1, /Expected a private key\./); 487 | 488 | rsa = new RsaWrap(); 489 | rsa.setPrivateKeyPem(fixture.PRIVATE_KEY); 490 | 491 | function f2() { 492 | rsa.sign("x", "x"); 493 | } 494 | assert.throws(f2, /Expected a 32-bit integer in args\[0]\./); 495 | 496 | function f3() { 497 | rsa.sign(1, "x"); 498 | } 499 | assert.throws(f3, /Expected a Buffer in args\[1]\./); 500 | 501 | function f4() { 502 | rsa.sign(1, new Buffer(2048)); 503 | } 504 | assert.throws(f4, /too big/); 505 | 506 | function f5() { 507 | rsa.sign(99999, new Buffer(16)); 508 | } 509 | assert.throws(f5, /unknown algorithm/); 510 | }); 511 | 512 | it('verify', function() { 513 | var rsa = new RsaWrap(); 514 | rsa.setPublicKeyPem(fixture.PUBLIC_KEY); 515 | 516 | var hash = new Buffer(fixture.FAKE_SHA256_TO_SIGN, fixture.HEX); 517 | var sig = new Buffer(fixture.FAKE_SHA256_SIGNATURE, fixture.HEX); 518 | assert.equal(rsa.verify(textToNid(fixture.SHA256), hash, sig), true); 519 | 520 | // Private keys should be able to verify too. 521 | hash = new Buffer(fixture.PLAINTEXT_SHA256, fixture.HEX); 522 | sig = new Buffer(fixture.PLAINTEXT_SHA256_SIGNATURE, fixture.HEX); 523 | assert.equal(rsa.verify(textToNid(fixture.SHA256), hash, sig), true); 524 | 525 | // Signature mismatch should return false (and not, e.g., throw). 526 | hash = new Buffer(fixture.FAKE_SHA256_TO_SIGN, fixture.HEX); 527 | sig = new Buffer(fixture.PLAINTEXT_SHA256_SIGNATURE, fixture.HEX); 528 | assert.equal(rsa.verify(textToNid(fixture.SHA256), hash, sig), false); 529 | }); 530 | 531 | it('fail_verify', function() { 532 | var rsa = new RsaWrap(); 533 | 534 | function f1() { 535 | rsa.verify(); 536 | } 537 | 538 | assert.throws(f1, /Key not yet set\./); 539 | 540 | rsa = new RsaWrap(); 541 | rsa.setPublicKeyPem(fixture.PUBLIC_KEY); 542 | 543 | function f2() { 544 | rsa.verify("x", "x", "x"); 545 | } 546 | assert.throws(f2, /Expected a 32-bit integer in args\[0]\./); 547 | 548 | function f3() { 549 | rsa.verify(1, "x", "x"); 550 | } 551 | assert.throws(f3, /Expected a Buffer in args\[1]\./); 552 | 553 | function f4() { 554 | rsa.verify(1, new Buffer(16), "x"); 555 | } 556 | assert.throws(f4, /Expected a Buffer in args\[2]\./); 557 | 558 | function f5() { 559 | var hash = new Buffer(10); 560 | var sig = new Buffer(5); 561 | hash.fill(0); 562 | sig.fill(0); 563 | rsa.verify(1, hash, sig); 564 | } 565 | assert.throws(f5, /wrong signature length/); 566 | 567 | function f6() { 568 | var buf = new Buffer(256); 569 | buf.fill(0); 570 | rsa.verify(1, new Buffer(10), buf); 571 | } 572 | assert.throws(f6, /padding_check/); 573 | 574 | function f7() { 575 | var hash = new Buffer(fixture.PLAINTEXT_SHA256, fixture.HEX); 576 | var sig = new Buffer(fixture.PLAINTEXT_SHA256_SIGNATURE, fixture.HEX); 577 | rsa.verify(textToNid(fixture.SHA1), hash, sig); 578 | } 579 | if (nodeVersion < 10) { 580 | assert.throws(f7, /algorithm mismatch/); 581 | } else { 582 | assert.ifError(f7(), true); 583 | } 584 | 585 | function f8() { 586 | var hash = new Buffer(fixture.PLAINTEXT_SHA256, fixture.HEX); 587 | var sig = new Buffer(fixture.PLAINTEXT_SHA256_SIGNATURE, fixture.HEX); 588 | rsa.verify(1234567, hash, sig); 589 | } 590 | assert.throws(f8, nodeVersion < 10 ? /algorithm mismatch/ : /unknown algorithm type/); 591 | }); 592 | 593 | it('textToNid', function() { 594 | // I don't think you can count on the return values being anything 595 | // other than integer values and that aliases should return equal 596 | // values. 597 | 598 | function verifyInt(value) { 599 | if (typeof value !== "number") { 600 | throw new Exception("Not a number: " + value); 601 | } 602 | 603 | if (value !== Math.floor(value)) { 604 | throw new Exception("Not an integer: " + value); 605 | } 606 | } 607 | 608 | verifyInt(textToNid("aes-128-ecb")); 609 | verifyInt(textToNid("md5")); 610 | verifyInt(textToNid("rsa")); 611 | verifyInt(textToNid("sha1")); 612 | verifyInt(textToNid("sha256")); 613 | verifyInt(textToNid("RSA-SHA256")); 614 | verifyInt(textToNid("pkcs7")); 615 | 616 | assert.equal(textToNid("RSA-SHA256"), textToNid("sha256WithRSAEncryption")); 617 | assert.equal(textToNid("AES-128-ECB"), textToNid("aes-128-ecb")); 618 | }); 619 | 620 | it('fail_textToNid', function() { 621 | function f1() { 622 | textToNid(); 623 | } 624 | assert.throws(f1, /Missing args\[0\]/); 625 | 626 | function f2() { 627 | textToNid(123); 628 | } 629 | assert.throws(f2, /Expected a string in args\[0\]/); 630 | 631 | function f3() { 632 | textToNid("blort"); 633 | } 634 | assert.throws(f3, /asn1/); 635 | }); 636 | 637 | it('PSSPadding', function() { 638 | _test_PSSPadding(ursaNative.RSA_PKCS1_SALT_LEN_HLEN); 639 | _test_PSSPadding(ursaNative.RSA_PKCS1_SALT_LEN_RECOVER); 640 | 641 | var rsa = new RsaWrap(); 642 | rsa.createPublicKeyFromComponents( 643 | new Buffer(fixture.PSS_MODULUS_HEX, fixture.HEX), 644 | new Buffer(fixture.EXPONENT_HEX, fixture.HEX)); 645 | 646 | var tvhash = new Buffer(fixture.PSS_MHASH_HEX, fixture.HEX); 647 | var tvem = new Buffer(fixture.PSS_EM_HEX, fixture.HEX); 648 | 649 | assert.equal(rsa.verifyPSSPadding( 650 | textToNid(fixture.SHA1), tvhash, tvem, ursaNative.RSA_PKCS1_SALT_LEN_HLEN), true); 651 | }); 652 | 653 | it('fail_PSSPadding', function() { 654 | var rsa = new RsaWrap(); 655 | 656 | function f1() { 657 | rsa.addPSSPadding(); 658 | } 659 | assert.throws(f1, /Key not yet set\./); 660 | rsa.setPublicKeyPem(fixture.PUBLIC_KEY); 661 | assert.throws(f1, /Not enough args\./); 662 | 663 | var nid = textToNid(fixture.SHA256); 664 | var hash = new Buffer(fixture.FAKE_SHA256_TO_SIGN, fixture.HEX); 665 | var slen = ursaNative.RSA_PKCS1_SALT_LEN_HLEN; 666 | 667 | function f2() { 668 | rsa.addPSSPadding("x", hash, slen); 669 | } 670 | assert.throws(f2, /Expected a 32-bit integer in args\[0\]\./); 671 | 672 | function f3() { 673 | rsa.addPSSPadding(nid, "x", slen); 674 | } 675 | assert.throws(f3, /Expected a Buffer in args\[1\]\./); 676 | 677 | function f4() { 678 | rsa.addPSSPadding(nid, hash, "x"); 679 | } 680 | assert.throws(f4, /Expected a 32-bit integer in args\[2\]\./); 681 | 682 | function f5() { 683 | rsa.addPSSPadding(nid, hash, 1000000); 684 | } 685 | assert.throws(f5, /data too large for key size/); 686 | 687 | function f6() { 688 | rsa.addPSSPadding(nid, hash, -3); 689 | } 690 | assert.throws(f6, /salt length check failed/); 691 | 692 | var em = rsa.addPSSPadding(nid, hash, slen); 693 | 694 | function f7() { 695 | rsa.verifyPSSPadding(); 696 | } 697 | assert.throws(f7, /Not enough args\./); 698 | 699 | function f8() { 700 | rsa.verifyPSSPadding("x", hash, em, slen); 701 | } 702 | assert.throws(f8, /Expected a 32-bit integer in args\[0\]\./); 703 | 704 | function f9() { 705 | rsa.verifyPSSPadding(nid, "x", em, slen); 706 | } 707 | assert.throws(f9, /Expected a Buffer in args\[1\]\./); 708 | 709 | function f10() { 710 | rsa.verifyPSSPadding(nid, hash, "x", slen); 711 | } 712 | assert.throws(f10, /Expected a Buffer in args\[2\]\./); 713 | 714 | function f11() { 715 | rsa.verifyPSSPadding(nid, hash, em, "x"); 716 | } 717 | assert.throws(f11, /Expected a 32-bit integer in args\[3\]\./); 718 | 719 | function f12() { 720 | rsa.verifyPSSPadding(nid, hash, em, 1000000); 721 | } 722 | assert.throws(f12, /data too large/); 723 | 724 | function f13() { 725 | rsa.verifyPSSPadding(nid, hash, em, -3); 726 | } 727 | assert.throws(f13, /salt length check failed/); 728 | 729 | em[em.length-1] ^= 2; 730 | 731 | function f14() { 732 | rsa.verifyPSSPadding(nid, hash, em, slen); 733 | } 734 | assert.throws(f14, /last octet invalid/); 735 | 736 | em[em.length-1] ^= 2; 737 | em[1] ^= 2; 738 | assert.throws(f14, /salt length recovery failed/); 739 | }); 740 | }) 741 | 742 | function _test_PSSPadding(slen) 743 | { 744 | var rsa = new RsaWrap(); 745 | rsa.setPublicKeyPem(fixture.PUBLIC_KEY); 746 | 747 | var nid = textToNid(fixture.SHA256); 748 | var hash = new Buffer(fixture.FAKE_SHA256_TO_SIGN, fixture.HEX); 749 | var em = rsa.addPSSPadding(nid, hash, slen); 750 | 751 | assert.equal(rsa.verifyPSSPadding(nid, hash, em, slen), true); 752 | } 753 | -------------------------------------------------------------------------------- /src/ursaNative.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Obvious Corporation. 2 | 3 | #include "ursaNative.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | using namespace v8; 14 | 15 | #ifdef _WIN32 16 | #include 17 | #define VAR_ARRAY(type, name, size) type *name = (type *)_alloca(size) 18 | #else 19 | #define VAR_ARRAY(type, name, size) type name[size] 20 | #endif 21 | 22 | #if OPENSSL_VERSION_NUMBER < 0x10100000L 23 | 24 | #include 25 | #include 26 | 27 | int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d) 28 | { 29 | /* If the fields n and e in r are NULL, the corresponding input 30 | * parameters MUST be non-NULL for n and e. d may be 31 | * left NULL (in case only the public key is used). 32 | */ 33 | if ((r->n == NULL && n == NULL) || (r->e == NULL && e == NULL)) 34 | return 0; 35 | 36 | if (n != NULL) 37 | { 38 | BN_free(r->n); 39 | r->n = n; 40 | } 41 | if (e != NULL) 42 | { 43 | BN_free(r->e); 44 | r->e = e; 45 | } 46 | if (d != NULL) 47 | { 48 | BN_free(r->d); 49 | r->d = d; 50 | } 51 | 52 | return 1; 53 | } 54 | 55 | int RSA_set0_factors(RSA *r, BIGNUM *p, BIGNUM *q) 56 | { 57 | /* If the fields p and q in r are NULL, the corresponding input 58 | * parameters MUST be non-NULL. 59 | */ 60 | if ((r->p == NULL && p == NULL) || (r->q == NULL && q == NULL)) 61 | return 0; 62 | 63 | if (p != NULL) 64 | { 65 | BN_free(r->p); 66 | r->p = p; 67 | } 68 | if (q != NULL) 69 | { 70 | BN_free(r->q); 71 | r->q = q; 72 | } 73 | 74 | return 1; 75 | } 76 | 77 | int RSA_set0_crt_params(RSA *r, BIGNUM *dmp1, BIGNUM *dmq1, BIGNUM *iqmp) 78 | { 79 | /* If the fields dmp1, dmq1 and iqmp in r are NULL, the corresponding input 80 | * parameters MUST be non-NULL. 81 | */ 82 | if ((r->dmp1 == NULL && dmp1 == NULL) || (r->dmq1 == NULL && dmq1 == NULL) || (r->iqmp == NULL && iqmp == NULL)) 83 | return 0; 84 | 85 | if (dmp1 != NULL) 86 | { 87 | BN_free(r->dmp1); 88 | r->dmp1 = dmp1; 89 | } 90 | if (dmq1 != NULL) 91 | { 92 | BN_free(r->dmq1); 93 | r->dmq1 = dmq1; 94 | } 95 | if (iqmp != NULL) 96 | { 97 | BN_free(r->iqmp); 98 | r->iqmp = iqmp; 99 | } 100 | 101 | return 1; 102 | } 103 | 104 | void RSA_get0_key(const RSA *r, 105 | const BIGNUM **n, const BIGNUM **e, const BIGNUM **d) 106 | { 107 | if (n != NULL) 108 | *n = r->n; 109 | if (e != NULL) 110 | *e = r->e; 111 | if (d != NULL) 112 | *d = r->d; 113 | } 114 | 115 | void RSA_get0_factors(const RSA *r, const BIGNUM **p, const BIGNUM **q) 116 | { 117 | if (p != NULL) 118 | *p = r->p; 119 | if (q != NULL) 120 | *q = r->q; 121 | } 122 | 123 | void RSA_get0_crt_params(const RSA *r, 124 | const BIGNUM **dmp1, const BIGNUM **dmq1, 125 | const BIGNUM **iqmp) 126 | { 127 | if (dmp1 != NULL) 128 | *dmp1 = r->dmp1; 129 | if (dmq1 != NULL) 130 | *dmq1 = r->dmq1; 131 | if (iqmp != NULL) 132 | *iqmp = r->iqmp; 133 | } 134 | 135 | void DSA_get0_pqg(const DSA *d, 136 | const BIGNUM **p, const BIGNUM **q, const BIGNUM **g) 137 | { 138 | if (p != NULL) 139 | *p = d->p; 140 | if (q != NULL) 141 | *q = d->q; 142 | if (g != NULL) 143 | *g = d->g; 144 | } 145 | 146 | int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g) 147 | { 148 | /* If the fields p, q and g in d are NULL, the corresponding input 149 | * parameters MUST be non-NULL. 150 | */ 151 | if ((d->p == NULL && p == NULL) || (d->q == NULL && q == NULL) || (d->g == NULL && g == NULL)) 152 | return 0; 153 | 154 | if (p != NULL) 155 | { 156 | BN_free(d->p); 157 | d->p = p; 158 | } 159 | if (q != NULL) 160 | { 161 | BN_free(d->q); 162 | d->q = q; 163 | } 164 | if (g != NULL) 165 | { 166 | BN_free(d->g); 167 | d->g = g; 168 | } 169 | 170 | return 1; 171 | } 172 | 173 | void DSA_get0_key(const DSA *d, 174 | const BIGNUM **pub_key, const BIGNUM **priv_key) 175 | { 176 | if (pub_key != NULL) 177 | *pub_key = d->pub_key; 178 | if (priv_key != NULL) 179 | *priv_key = d->priv_key; 180 | } 181 | 182 | int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key) 183 | { 184 | /* If the field pub_key in d is NULL, the corresponding input 185 | * parameters MUST be non-NULL. The priv_key field may 186 | * be left NULL. 187 | */ 188 | if (d->pub_key == NULL && pub_key == NULL) 189 | return 0; 190 | 191 | if (pub_key != NULL) 192 | { 193 | BN_free(d->pub_key); 194 | d->pub_key = pub_key; 195 | } 196 | if (priv_key != NULL) 197 | { 198 | BN_free(d->priv_key); 199 | d->priv_key = priv_key; 200 | } 201 | 202 | return 1; 203 | } 204 | 205 | void DSA_SIG_get0(const DSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps) 206 | { 207 | if (pr != NULL) 208 | *pr = sig->r; 209 | if (ps != NULL) 210 | *ps = sig->s; 211 | } 212 | 213 | int DSA_SIG_set0(DSA_SIG *sig, BIGNUM *r, BIGNUM *s) 214 | { 215 | if (r == NULL || s == NULL) 216 | return 0; 217 | BN_clear_free(sig->r); 218 | BN_clear_free(sig->s); 219 | sig->r = r; 220 | sig->s = s; 221 | return 1; 222 | } 223 | 224 | void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps) 225 | { 226 | if (pr != NULL) 227 | *pr = sig->r; 228 | if (ps != NULL) 229 | *ps = sig->s; 230 | } 231 | 232 | int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s) 233 | { 234 | if (r == NULL || s == NULL) 235 | return 0; 236 | BN_clear_free(sig->r); 237 | BN_clear_free(sig->s); 238 | sig->r = r; 239 | sig->s = s; 240 | return 1; 241 | } 242 | 243 | void DH_get0_pqg(const DH *dh, 244 | const BIGNUM **p, const BIGNUM **q, const BIGNUM **g) 245 | { 246 | if (p != NULL) 247 | *p = dh->p; 248 | if (q != NULL) 249 | *q = dh->q; 250 | if (g != NULL) 251 | *g = dh->g; 252 | } 253 | 254 | int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g) 255 | { 256 | /* If the fields p and g in d are NULL, the corresponding input 257 | * parameters MUST be non-NULL. q may remain NULL. 258 | */ 259 | if ((dh->p == NULL && p == NULL) || (dh->g == NULL && g == NULL)) 260 | return 0; 261 | 262 | if (p != NULL) 263 | { 264 | BN_free(dh->p); 265 | dh->p = p; 266 | } 267 | if (q != NULL) 268 | { 269 | BN_free(dh->q); 270 | dh->q = q; 271 | } 272 | if (g != NULL) 273 | { 274 | BN_free(dh->g); 275 | dh->g = g; 276 | } 277 | 278 | if (q != NULL) 279 | { 280 | dh->length = BN_num_bits(q); 281 | } 282 | 283 | return 1; 284 | } 285 | 286 | void DH_get0_key(const DH *dh, const BIGNUM **pub_key, const BIGNUM **priv_key) 287 | { 288 | if (pub_key != NULL) 289 | *pub_key = dh->pub_key; 290 | if (priv_key != NULL) 291 | *priv_key = dh->priv_key; 292 | } 293 | 294 | int DH_set0_key(DH *dh, BIGNUM *pub_key, BIGNUM *priv_key) 295 | { 296 | /* If the field pub_key in dh is NULL, the corresponding input 297 | * parameters MUST be non-NULL. The priv_key field may 298 | * be left NULL. 299 | */ 300 | if (dh->pub_key == NULL && pub_key == NULL) 301 | return 0; 302 | 303 | if (pub_key != NULL) 304 | { 305 | BN_free(dh->pub_key); 306 | dh->pub_key = pub_key; 307 | } 308 | if (priv_key != NULL) 309 | { 310 | BN_free(dh->priv_key); 311 | dh->priv_key = priv_key; 312 | } 313 | 314 | return 1; 315 | } 316 | 317 | int DH_set_length(DH *dh, long length) 318 | { 319 | dh->length = length; 320 | return 1; 321 | } 322 | 323 | const unsigned char *EVP_CIPHER_CTX_iv(const EVP_CIPHER_CTX *ctx) 324 | { 325 | return ctx->iv; 326 | } 327 | 328 | unsigned char *EVP_CIPHER_CTX_iv_noconst(EVP_CIPHER_CTX *ctx) 329 | { 330 | return ctx->iv; 331 | } 332 | #endif 333 | 334 | Nan::Persistent constructor; 335 | 336 | /* 337 | * Initialization and binding 338 | */ 339 | #define NanThrowError(err) Nan::ThrowError(err); 340 | #define NanNewBufferHandle(length) Nan::NewBuffer(length).ToLocalChecked() 341 | #define args info 342 | #define NanScope() Nan::HandleScope scope 343 | #define NanReturnUndefined() \ 344 | { \ 345 | info.GetReturnValue().Set(Nan::Undefined()); \ 346 | return; \ 347 | } 348 | #define NanNew Nan::New 349 | #define NanReturnValue(value) \ 350 | { \ 351 | info.GetReturnValue().Set(value); \ 352 | return; \ 353 | } 354 | #define NanFalse() Nan::False() 355 | #define NanTrue() Nan::True() 356 | 357 | #define RSA_PKCS1_SALT_LEN_HLEN -1 358 | #define RSA_PKCS1_SALT_LEN_MAX -2 359 | #define RSA_PKCS1_SALT_LEN_RECOVER -2 360 | 361 | /** 362 | * Top-level initialization function. 363 | */ 364 | void init(Local target) 365 | { 366 | NODE_DEFINE_CONSTANT(target, RSA_NO_PADDING); 367 | NODE_DEFINE_CONSTANT(target, RSA_PKCS1_PADDING); 368 | NODE_DEFINE_CONSTANT(target, RSA_PKCS1_OAEP_PADDING); 369 | NODE_DEFINE_CONSTANT(target, RSA_PKCS1_SALT_LEN_HLEN); 370 | NODE_DEFINE_CONSTANT(target, RSA_PKCS1_SALT_LEN_MAX); 371 | NODE_DEFINE_CONSTANT(target, RSA_PKCS1_SALT_LEN_RECOVER); 372 | 373 | RsaWrap::InitClass(target); 374 | 375 | #ifdef _WIN32 376 | // On Windows, we can't use Node's OpenSSL, so we link 377 | // to a standalone OpenSSL library. Therefore, we need 378 | // to initialize OpenSSL separately. 379 | 380 | //TODO: Do I need to free these? 381 | //I'm not sure where to call ERR_free_strings() and EVP_cleanup() 382 | OpenSSL_add_all_digests(); 383 | OpenSSL_add_all_algorithms(); 384 | OpenSSL_add_all_ciphers(); 385 | ERR_load_crypto_strings(); 386 | #endif 387 | } 388 | 389 | NODE_MODULE(ursaNative, init) 390 | 391 | /* 392 | * Helper functions 393 | */ 394 | 395 | /** 396 | * Schedule the current SSL error as a higher-level exception. 397 | */ 398 | static void scheduleSslException() 399 | { 400 | char *err = ERR_error_string(ERR_get_error(), NULL); 401 | ERR_clear_error(); 402 | NanThrowError(err); 403 | } 404 | 405 | /** 406 | * Schedule an "allocation failed" exception. This (tries) to allocate 407 | * as well, which very well could (probably will) fail too, but it's the 408 | * best we can do in a bad situation. 409 | */ 410 | static void scheduleAllocException() 411 | { 412 | NanThrowError("Allocation failed."); 413 | } 414 | 415 | /** 416 | * Convert the given (BIGNUM *) to a Buffer of unsigned big-endian 417 | * contents. Returns a Buffer-containing handle on success. Schedules an 418 | * exception and returns Undefined() on failure. 419 | */ 420 | static Nan::NAN_METHOD_RETURN_TYPE bignumToBuffer(Nan::NAN_METHOD_ARGS_TYPE args, 421 | BIGNUM *number) 422 | { 423 | int length = BN_num_bytes(number); 424 | Local result = NanNewBufferHandle(length); 425 | 426 | if (BN_bn2bin(number, (unsigned char *)node::Buffer::Data(result)) < 0) 427 | { 428 | scheduleSslException(); 429 | NanReturnUndefined(); 430 | } 431 | 432 | NanReturnValue(result); 433 | } 434 | 435 | /** 436 | * Convert the given memory-based (BIO *) to a Buffer of its contents. 437 | * Returns a Buffer-containing handle on success. Schedules an 438 | * exception and returns Undefined() on failure. In either case, the 439 | * BIO is freed by the time this function returns. 440 | * 441 | * As a special case to help with error handling, if given a NULL 442 | * argument, this simply returns Undefined(). 443 | */ 444 | static Nan::NAN_METHOD_RETURN_TYPE bioToBuffer(Nan::NAN_METHOD_ARGS_TYPE args, 445 | BIO *bio) 446 | { 447 | if (bio == NULL) 448 | { 449 | NanReturnUndefined(); 450 | } 451 | 452 | char *data; 453 | long length = BIO_get_mem_data(bio, &data); 454 | Local result = NanNewBufferHandle(length); 455 | 456 | if (result.IsEmpty()) 457 | { 458 | scheduleAllocException(); 459 | BIO_vfree(bio); 460 | NanReturnUndefined(); 461 | } 462 | 463 | memcpy(node::Buffer::Data(result), data, length); 464 | BIO_vfree(bio); 465 | 466 | NanReturnValue(result); 467 | } 468 | 469 | /** 470 | * Get a Buffer out of args[0], converted to a freshly-allocated 471 | * memory BIO. Returns a non-null pointer on success. On failure, 472 | * schedules an exception and returns NULL. 473 | */ 474 | static BIO *getArg0Bio(const Local buf) 475 | { 476 | if (!node::Buffer::HasInstance(buf)) 477 | { 478 | NanThrowError("Expected a Buffer in args[0]."); 479 | return NULL; 480 | } 481 | 482 | char *data = node::Buffer::Data(buf); 483 | ssize_t length = node::Buffer::Length(buf); 484 | BIO *bio = BIO_new_mem_buf(data, length); 485 | 486 | if (bio == NULL) 487 | { 488 | scheduleSslException(); 489 | } 490 | 491 | return bio; 492 | } 493 | 494 | static BIGNUM *getArgXBigNum(const Local buf) 495 | { 496 | if (!node::Buffer::HasInstance(buf)) 497 | { 498 | NanThrowError("Expected a Buffer."); 499 | return NULL; 500 | } 501 | char *data = node::Buffer::Data(buf); 502 | ssize_t length = node::Buffer::Length(buf); 503 | 504 | return BN_bin2bn(reinterpret_cast(data), length, NULL); 505 | } 506 | 507 | /** 508 | * Get a Buffer out of args[1], converted to a freshly-allocated (char 509 | * *). Returns a non-null pointer on success. On failure, schedules an 510 | * exception and returns NULL. 511 | */ 512 | static char *copyBufferToCharStar(const Local buf) 513 | { 514 | 515 | if (!node::Buffer::HasInstance(buf)) 516 | { 517 | return NULL; 518 | } 519 | 520 | char *data = node::Buffer::Data(buf); 521 | ssize_t length = node::Buffer::Length(buf); 522 | char *result = (char *)malloc(length + 1); 523 | 524 | if (result == NULL) 525 | { 526 | scheduleAllocException(); 527 | return NULL; 528 | } 529 | 530 | memcpy(result, data, length); 531 | result[length] = '\0'; 532 | return result; 533 | } 534 | 535 | /** 536 | * Get a string out of args[] at the given index, converted to a 537 | * freshly-allocated (char *). Returns a non-null pointer on 538 | * success. On failure, schedules an exception and returns NULL. 539 | */ 540 | static char *copyBufferToUtf8String(const Local str) 541 | { 542 | // static char *getArgString(const Arguments& args, int index) { 543 | int length = str->Utf8Length(); 544 | char *result = (char *)malloc(length + 1); 545 | 546 | if (result == NULL) 547 | { 548 | scheduleAllocException(); 549 | return NULL; 550 | } 551 | 552 | result[length] = 'x'; // Set up a small sanity check (see below). 553 | str->WriteUtf8(result, length + 1); 554 | 555 | if (result[length] != '\0') 556 | { 557 | const char *message = "String conversion failed."; 558 | NanThrowError(message); 559 | free(result); 560 | return NULL; 561 | } 562 | 563 | return result; 564 | } 565 | 566 | /** 567 | * Generate a key, using one of the two possibly-available functions. 568 | * This prefers the newer function, if available. 569 | */ 570 | static RSA *generateKey(int num, unsigned long e) 571 | { 572 | #if OPENSSL_VERSION_NUMBER < 0x009080001 573 | return RSA_generate_key(num, e, NULL, NULL); 574 | #else 575 | BIGNUM *eBig = BN_new(); 576 | 577 | if (eBig == NULL) 578 | { 579 | return NULL; 580 | } 581 | 582 | if (!BN_set_word(eBig, e)) 583 | { 584 | BN_free(eBig); 585 | return NULL; 586 | } 587 | 588 | RSA *result = RSA_new(); 589 | 590 | if (result == NULL) 591 | { 592 | BN_free(eBig); 593 | return NULL; 594 | } 595 | 596 | if (RSA_generate_key_ex(result, num, eBig, NULL) < 0) 597 | { 598 | RSA_free(result); 599 | result = NULL; 600 | } 601 | 602 | BN_free(eBig); 603 | return result; 604 | #endif 605 | } 606 | 607 | /* 608 | * Utility function implementation 609 | */ 610 | 611 | /** 612 | * Call the OpenSSL function OBJ_txt2nid() on the given string. 613 | * This returns a number representing the text that, as far as I 614 | * (danfuzz) know, is not necessarily stable across versions of 615 | * OpenSSL, so it's only safe to use transiently. 616 | */ 617 | NAN_METHOD(TextToNid) 618 | { 619 | NanScope(); 620 | 621 | if (args.Length() < 1) 622 | { 623 | NanThrowError("Missing args[0]."); 624 | NanReturnUndefined(); 625 | } 626 | 627 | if (!args[0]->IsString()) 628 | { 629 | NanThrowError("Expected a string in args[0]."); 630 | NanReturnUndefined(); 631 | } 632 | 633 | Local str = args[0].As(); 634 | char *name = copyBufferToUtf8String(str); 635 | if (name == NULL) 636 | { 637 | NanReturnUndefined(); 638 | } 639 | 640 | int nid = OBJ_txt2nid(name); 641 | free(name); 642 | 643 | if (nid == NID_undef) 644 | { 645 | scheduleSslException(); 646 | NanReturnUndefined(); 647 | } 648 | 649 | NanReturnValue(NanNew(nid)); 650 | } 651 | 652 | /* 653 | * RsaWrap implementation 654 | */ 655 | 656 | /** 657 | * Initialize the bindings for this class. 658 | */ 659 | void RsaWrap::InitClass(Local target) 660 | { 661 | Local className = NanNew("RsaWrap").ToLocalChecked(); 662 | 663 | // Basic instance setup 664 | Local tpl = NanNew(New); 665 | 666 | tpl->SetClassName(className); 667 | tpl->InstanceTemplate()->SetInternalFieldCount(1); // req'd by ObjectWrap 668 | 669 | Nan::SetPrototypeMethod(tpl, "generatePrivateKey", GeneratePrivateKey); 670 | Nan::SetPrototypeMethod(tpl, "getExponent", GetExponent); 671 | Nan::SetPrototypeMethod(tpl, "getPrivateExponent", GetPrivateExponent); 672 | Nan::SetPrototypeMethod(tpl, "getModulus", GetModulus); 673 | Nan::SetPrototypeMethod(tpl, "getPrivateKeyPem", GetPrivateKeyPem); 674 | Nan::SetPrototypeMethod(tpl, "getPublicKeyPem", GetPublicKeyPem); 675 | Nan::SetPrototypeMethod(tpl, "privateDecrypt", PrivateDecrypt); 676 | Nan::SetPrototypeMethod(tpl, "privateEncrypt", PrivateEncrypt); 677 | Nan::SetPrototypeMethod(tpl, "publicDecrypt", PublicDecrypt); 678 | Nan::SetPrototypeMethod(tpl, "publicEncrypt", PublicEncrypt); 679 | Nan::SetPrototypeMethod(tpl, "setPrivateKeyPem", SetPrivateKeyPem); 680 | Nan::SetPrototypeMethod(tpl, "setPublicKeyPem", SetPublicKeyPem); 681 | Nan::SetPrototypeMethod(tpl, "sign", Sign); 682 | Nan::SetPrototypeMethod(tpl, "verify", Verify); 683 | Nan::SetPrototypeMethod(tpl, "createPrivateKeyFromComponents", CreatePrivateKeyFromComponents); 684 | Nan::SetPrototypeMethod(tpl, "createPublicKeyFromComponents", CreatePublicKeyFromComponents); 685 | Nan::SetPrototypeMethod(tpl, "openPublicSshKey", OpenPublicSshKey); 686 | Nan::SetPrototypeMethod(tpl, "addPSSPadding", AddPSSPadding); 687 | Nan::SetPrototypeMethod(tpl, "verifyPSSPadding", VerifyPSSPadding); 688 | 689 | // Store the constructor in the target bindings. 690 | target->Set(NanNew("RsaWrap").ToLocalChecked(), tpl->GetFunction()); 691 | constructor.Reset(tpl->GetFunction()); 692 | 693 | target->Set(NanNew("textToNid").ToLocalChecked(), Nan::New(TextToNid)->GetFunction()); 694 | } 695 | 696 | /** 697 | * Straightforward constructor. Nothing much to initialize, other than 698 | * to ensure that our one instance variable is sanely NULLed. 699 | */ 700 | RsaWrap::RsaWrap() 701 | { 702 | rsa = NULL; 703 | } 704 | 705 | /** 706 | * Destructor, which is called automatically via the ObjectWrap mechanism 707 | * when the corresponding high-level object gets gc'ed. 708 | */ 709 | RsaWrap::~RsaWrap() 710 | { 711 | if (rsa != NULL) 712 | { 713 | RSA_free(rsa); 714 | } 715 | } 716 | 717 | NAN_METHOD(RsaWrap::OpenPublicSshKey) 718 | { 719 | NanScope(); 720 | RsaWrap *obj = ObjectWrap::Unwrap(args.Holder()); 721 | obj = expectUnset(obj); 722 | 723 | if (args.Length() < 2) 724 | { 725 | NanThrowError("Not enough args."); 726 | NanReturnUndefined(); 727 | } 728 | 729 | Local obj_n = args[0].As(); 730 | Local obj_e = args[1].As(); 731 | int n_length = node::Buffer::Length(obj_n); 732 | int e_length = node::Buffer::Length(obj_e); 733 | unsigned char *data_n = (unsigned char *)malloc(n_length); 734 | unsigned char *data_e = (unsigned char *)malloc(e_length); 735 | memcpy(data_n, node::Buffer::Data(obj_n), n_length); 736 | memcpy(data_e, node::Buffer::Data(obj_e), e_length); 737 | 738 | if (obj->rsa == NULL) 739 | { 740 | obj->rsa = RSA_new(); 741 | } 742 | 743 | obj->rsa_n = BN_new(); 744 | obj->rsa_e = BN_new(); 745 | 746 | BN_bin2bn(data_n, n_length, obj->rsa_n); 747 | BN_bin2bn(data_e, e_length, obj->rsa_e); 748 | RSA_set0_key(obj->rsa, obj->rsa_n, obj->rsa_e, NULL); 749 | 750 | free(data_n); 751 | free(data_e); 752 | NanReturnUndefined(); 753 | } 754 | 755 | /** 756 | * Get an (RsaWrap *) out of the given arguments, expecting the 757 | * underlying (RSA *) to be non-null and more specifically a private 758 | * key. Returns a non-null pointer on success. On failure, schedules 759 | * an exception and returns null. 760 | */ 761 | RsaWrap *RsaWrap::expectPrivateKey(RsaWrap *obj) 762 | { 763 | obj = expectSet(obj); 764 | 765 | if (obj != NULL) { 766 | RSA_get0_key(obj->rsa, NULL, NULL, (const BIGNUM **)&obj->rsa_d); 767 | } 768 | 769 | // The "d" field should always be set on a private key and never 770 | // set on a public key. 771 | if ((obj == NULL) || (obj->rsa_d != NULL)) 772 | { 773 | return obj; 774 | } 775 | 776 | NanThrowError("Expected a private key."); 777 | return NULL; 778 | } 779 | 780 | /** 781 | * Get an (RsaWrap *) out of the given arguments, expecting the underlying 782 | * (RSA *) to be non-null. Returns a non-null pointer on success. On failure, 783 | * schedules an exception and returns null. 784 | */ 785 | RsaWrap *RsaWrap::expectSet(RsaWrap *obj) 786 | { 787 | 788 | if (obj->rsa != NULL) 789 | { 790 | return obj; 791 | } 792 | 793 | NanThrowError("Key not yet set."); 794 | return NULL; 795 | } 796 | 797 | /** 798 | * Get an (RsaWrap *) out of the given arguments, expecting the underlying 799 | * (RSA *) to be null. Returns a non-null pointer on success. On failure, 800 | * schedules an exception and returns null. 801 | */ 802 | RsaWrap *RsaWrap::expectUnset(RsaWrap *obj) 803 | { 804 | 805 | if (obj->rsa == NULL) 806 | { 807 | return obj; 808 | } 809 | 810 | NanThrowError("Key already set."); 811 | return NULL; 812 | } 813 | 814 | /** 815 | * Construct an empty instance. 816 | */ 817 | NAN_METHOD(RsaWrap::New) 818 | { 819 | NanScope(); 820 | 821 | RsaWrap *obj = new RsaWrap(); 822 | obj->Wrap(args.This()); 823 | 824 | NanReturnValue(args.This()); 825 | } 826 | 827 | /** 828 | * Set the underlying RSA struct to a newly-generated key pair. 829 | */ 830 | NAN_METHOD(RsaWrap::GeneratePrivateKey) 831 | { 832 | NanScope(); 833 | 834 | RsaWrap *obj = ObjectWrap::Unwrap(args.Holder()); 835 | obj = expectUnset(obj); 836 | if (obj == NULL) 837 | { 838 | NanReturnUndefined(); 839 | } 840 | 841 | // Sadly the change in V8 args type signature makes this messier now. 842 | if (args.Length() < 1) 843 | { 844 | NanThrowError("Missing args[0]."); 845 | NanReturnUndefined(); 846 | } 847 | 848 | if (!args[0]->IsInt32()) 849 | { 850 | NanThrowError("Expected a 32-bit integer in args[0]."); 851 | NanReturnUndefined(); 852 | } 853 | 854 | if (args.Length() < 2) 855 | { 856 | NanThrowError("Missing args[1]."); 857 | NanReturnUndefined(); 858 | } 859 | 860 | if (!args[1]->IsInt32()) 861 | { 862 | NanThrowError("Expected a 32-bit integer in args[1]."); 863 | NanReturnUndefined(); 864 | } 865 | 866 | int modulusBits = args[0]->Uint32Value(); 867 | int exponent = args[1]->Uint32Value(); 868 | 869 | // Sanity-check the arguments, since (as of this writing) OpenSSL 870 | // either doesn't check, or at least doesn't consistently check: 871 | // 872 | // * The modulus bit count must be >= 512. Really, it just has to 873 | // be a positive integer, but anything less than 512 is a 874 | // horrendously bad idea. 875 | // 876 | // * The exponend must be positive and odd. 877 | 878 | if (modulusBits < 512) 879 | { 880 | NanThrowError("Expected modulus bit count >= 512."); 881 | NanReturnUndefined(); 882 | } 883 | 884 | if (exponent <= 0) 885 | { 886 | NanThrowError("Expected positive exponent."); 887 | NanReturnUndefined(); 888 | } 889 | 890 | if ((exponent & 1) == 0) 891 | { 892 | NanThrowError("Expected odd exponent."); 893 | NanReturnUndefined(); 894 | } 895 | 896 | obj->rsa = generateKey(modulusBits, (unsigned long)exponent); 897 | 898 | if (obj->rsa == NULL) 899 | { 900 | scheduleSslException(); 901 | } 902 | 903 | NanReturnUndefined(); 904 | } 905 | 906 | /** 907 | * Get the public exponent of the underlying RSA object. The return 908 | * value is a Buffer containing the unsigned number in big-endian 909 | * order. 910 | */ 911 | NAN_METHOD(RsaWrap::GetExponent) 912 | { 913 | NanScope(); 914 | 915 | RsaWrap *obj = ObjectWrap::Unwrap(args.Holder()); 916 | obj = expectSet(obj); 917 | if (obj == NULL) 918 | { 919 | NanReturnUndefined(); 920 | } 921 | 922 | RSA_get0_key(obj->rsa, NULL, (const BIGNUM **)&obj->rsa_e, NULL); 923 | bignumToBuffer(args, obj->rsa_e); 924 | } 925 | 926 | /** 927 | * Get the private exponent of the underlying RSA object. The return 928 | * value is a Buffer containing the unsigned number in big-endian 929 | * order. The returned exponent is not encrypted in any way, 930 | * so this should be used with caution. 931 | */ 932 | NAN_METHOD(RsaWrap::GetPrivateExponent) 933 | { 934 | NanScope(); 935 | 936 | RsaWrap *obj = ObjectWrap::Unwrap(args.Holder()); 937 | obj = expectPrivateKey(obj); 938 | if (obj == NULL) 939 | { 940 | NanReturnUndefined(); 941 | } 942 | 943 | RSA_get0_key(obj->rsa, NULL, NULL, (const BIGNUM **)&obj->rsa_d); 944 | bignumToBuffer(args, obj->rsa_d); 945 | } 946 | 947 | /** 948 | * Get the public modulus of the underlying RSA object. The return 949 | * value is a Buffer containing the unsigned number in big-endian 950 | * order. 951 | */ 952 | NAN_METHOD(RsaWrap::GetModulus) 953 | { 954 | NanScope(); 955 | 956 | RsaWrap *obj = ObjectWrap::Unwrap(args.Holder()); 957 | obj = expectSet(obj); 958 | if (obj == NULL) 959 | { 960 | NanReturnUndefined(); 961 | } 962 | 963 | RSA_get0_key(obj->rsa, (const BIGNUM **)&obj->rsa_n, NULL, NULL); 964 | bignumToBuffer(args, obj->rsa_n); 965 | } 966 | 967 | /** 968 | * Get the private key of the underlying RSA object as a file 969 | * in PEM format. The return value is a Buffer containing the 970 | * file contents (in ASCII / UTF8). Note: This does not do any 971 | * encryption of the results. 972 | */ 973 | NAN_METHOD(RsaWrap::GetPrivateKeyPem) 974 | { 975 | NanScope(); 976 | 977 | RsaWrap *obj = ObjectWrap::Unwrap(args.Holder()); 978 | obj = expectPrivateKey(obj); 979 | 980 | if (obj == NULL) 981 | { 982 | NanReturnUndefined(); 983 | } 984 | 985 | BIO *bio = BIO_new(BIO_s_mem()); 986 | if (bio == NULL) 987 | { 988 | scheduleSslException(); 989 | NanReturnUndefined(); 990 | } 991 | 992 | char *password = NULL; 993 | int passwordLen = 0; 994 | const EVP_CIPHER *cipher = NULL; 995 | 996 | if (args.Length() > 0) 997 | { 998 | Local pstr = args[0].As(); 999 | password = copyBufferToUtf8String(pstr); 1000 | 1001 | Local cstr = args[1].As(); 1002 | char *cipherName = copyBufferToUtf8String(cstr); 1003 | cipher = EVP_get_cipherbyname(cipherName); 1004 | free(cipherName); 1005 | } 1006 | 1007 | if (password != NULL) 1008 | { 1009 | passwordLen = (int)strlen(password); 1010 | } 1011 | 1012 | if (!PEM_write_bio_RSAPrivateKey(bio, obj->rsa, 1013 | cipher, (unsigned char *)password, 1014 | passwordLen, NULL, NULL)) 1015 | { 1016 | scheduleSslException(); 1017 | BIO_vfree(bio); 1018 | free(password); 1019 | NanReturnUndefined(); 1020 | } 1021 | 1022 | free(password); 1023 | bioToBuffer(args, bio); 1024 | } 1025 | 1026 | /** 1027 | * Get the public key of the underlying RSA object as a file 1028 | * in PEM format. The return value is a Buffer containing the 1029 | * file contents (in ASCII / UTF8). 1030 | */ 1031 | NAN_METHOD(RsaWrap::GetPublicKeyPem) 1032 | { 1033 | NanScope(); 1034 | 1035 | RsaWrap *obj = ObjectWrap::Unwrap(args.Holder()); 1036 | obj = expectSet(obj); 1037 | if (obj == NULL) 1038 | { 1039 | NanReturnUndefined(); 1040 | } 1041 | 1042 | BIO *bio = BIO_new(BIO_s_mem()); 1043 | if (bio == NULL) 1044 | { 1045 | scheduleSslException(); 1046 | NanReturnUndefined(); 1047 | } 1048 | 1049 | if (!PEM_write_bio_RSA_PUBKEY(bio, obj->rsa)) 1050 | { 1051 | scheduleSslException(); 1052 | BIO_vfree(bio); 1053 | NanReturnUndefined(); 1054 | } 1055 | 1056 | bioToBuffer(args, bio); 1057 | } 1058 | 1059 | /** 1060 | * Perform decryption on the given buffer using the RSA key, which 1061 | * must be a private key, and padding mode. 1062 | */ 1063 | NAN_METHOD(RsaWrap::PrivateDecrypt) 1064 | { 1065 | NanScope(); 1066 | 1067 | RsaWrap *obj = ObjectWrap::Unwrap(args.Holder()); 1068 | obj = expectPrivateKey(obj); 1069 | 1070 | if (obj == NULL) 1071 | { 1072 | NanReturnUndefined(); 1073 | } 1074 | 1075 | if (args.Length() < 2) 1076 | { 1077 | NanThrowError("Not enough args."); 1078 | NanReturnUndefined(); 1079 | } 1080 | 1081 | Local buffer = args[0].As(); 1082 | if (!node::Buffer::HasInstance(buffer)) 1083 | { 1084 | NanThrowError("Expected a Buffer in args[0]."); 1085 | NanReturnUndefined(); 1086 | } 1087 | 1088 | size_t length = node::Buffer::Length(buffer); 1089 | char *data = node::Buffer::Data(buffer); 1090 | if (data == NULL) 1091 | { 1092 | NanReturnUndefined(); 1093 | } 1094 | 1095 | int rsaLength = RSA_size(obj->rsa); 1096 | VAR_ARRAY(unsigned char, buf, rsaLength); 1097 | 1098 | if (!args[1]->IsInt32()) 1099 | { 1100 | NanThrowError("Expected a 32-bit integer in args[1]."); 1101 | NanReturnUndefined(); 1102 | } 1103 | int padding = args[1]->Uint32Value(); 1104 | 1105 | int bufLength = RSA_private_decrypt(length, (unsigned char *)data, 1106 | buf, obj->rsa, padding); 1107 | 1108 | if (bufLength < 0) 1109 | { 1110 | scheduleSslException(); 1111 | NanReturnUndefined(); 1112 | } 1113 | 1114 | Local result = NanNewBufferHandle(bufLength); 1115 | memcpy(node::Buffer::Data(result), buf, bufLength); 1116 | NanReturnValue(result); 1117 | } 1118 | 1119 | /** 1120 | * Perform encryption on the given buffer using the RSA key, which 1121 | * must be private, and padding mode. 1122 | */ 1123 | NAN_METHOD(RsaWrap::PrivateEncrypt) 1124 | { 1125 | NanScope(); 1126 | 1127 | RsaWrap *obj = ObjectWrap::Unwrap(args.Holder()); 1128 | obj = expectPrivateKey(obj); 1129 | if (obj == NULL) 1130 | { 1131 | NanReturnUndefined(); 1132 | } 1133 | 1134 | if (args.Length() < 2) 1135 | { 1136 | NanThrowError("Not enough args."); 1137 | NanReturnUndefined(); 1138 | } 1139 | 1140 | Local buffer = args[0].As(); 1141 | if (!node::Buffer::HasInstance(buffer)) 1142 | { 1143 | NanThrowError("Expected a Buffer in args[0]."); 1144 | NanReturnUndefined(); 1145 | } 1146 | size_t length = node::Buffer::Length(buffer); 1147 | char *data = node::Buffer::Data(buffer); 1148 | if (data == NULL) 1149 | { 1150 | NanReturnUndefined(); 1151 | } 1152 | 1153 | int rsaLength = RSA_size(obj->rsa); 1154 | Local result = NanNewBufferHandle(rsaLength); 1155 | 1156 | if (!args[1]->IsInt32()) 1157 | { 1158 | NanThrowError("Expected a 32-bit integer in args[1]."); 1159 | NanReturnUndefined(); 1160 | } 1161 | int padding = args[1]->Uint32Value(); 1162 | 1163 | int ret = RSA_private_encrypt(length, (unsigned char *)data, 1164 | (unsigned char *)node::Buffer::Data(result), 1165 | obj->rsa, padding); 1166 | 1167 | if (ret < 0) 1168 | { 1169 | scheduleSslException(); 1170 | NanReturnUndefined(); 1171 | } 1172 | 1173 | NanReturnValue(result); 1174 | } 1175 | 1176 | /** 1177 | * Perform decryption on the given buffer using the (public aspect of 1178 | * the) RSA key, and padding mode. 1179 | */ 1180 | NAN_METHOD(RsaWrap::PublicDecrypt) 1181 | { 1182 | NanScope(); 1183 | 1184 | RsaWrap *obj = ObjectWrap::Unwrap(args.Holder()); 1185 | obj = expectSet(obj); 1186 | if (obj == NULL) 1187 | { 1188 | NanReturnUndefined(); 1189 | } 1190 | 1191 | if (args.Length() < 2) 1192 | { 1193 | NanThrowError("Not enough args."); 1194 | NanReturnUndefined(); 1195 | } 1196 | 1197 | Local buffer = args[0].As(); 1198 | if (!node::Buffer::HasInstance(buffer)) 1199 | { 1200 | NanThrowError("Expected a Buffer in args[0]."); 1201 | NanReturnUndefined(); 1202 | } 1203 | size_t length = node::Buffer::Length(buffer); 1204 | char *data = node::Buffer::Data(buffer); 1205 | if (data == NULL) 1206 | { 1207 | NanReturnUndefined(); 1208 | } 1209 | 1210 | int rsaLength = RSA_size(obj->rsa); 1211 | VAR_ARRAY(unsigned char, buf, rsaLength); 1212 | 1213 | if (!args[1]->IsInt32()) 1214 | { 1215 | NanThrowError("Expected a 32-bit integer in args[1]."); 1216 | NanReturnUndefined(); 1217 | } 1218 | int padding = args[1]->Uint32Value(); 1219 | 1220 | int bufLength = RSA_public_decrypt(length, (unsigned char *)data, 1221 | buf, obj->rsa, padding); 1222 | 1223 | if (bufLength < 0) 1224 | { 1225 | scheduleSslException(); 1226 | NanReturnUndefined(); 1227 | } 1228 | 1229 | Local result = NanNewBufferHandle(bufLength); 1230 | memcpy(node::Buffer::Data(result), buf, bufLength); 1231 | NanReturnValue(result); 1232 | } 1233 | 1234 | /** 1235 | * Perform encryption on the given buffer using the public (aspect of the) 1236 | * RSA key, and padding mode. 1237 | */ 1238 | NAN_METHOD(RsaWrap::PublicEncrypt) 1239 | { 1240 | NanScope(); 1241 | 1242 | RsaWrap *obj = ObjectWrap::Unwrap(args.Holder()); 1243 | obj = expectSet(obj); 1244 | if (obj == NULL) 1245 | { 1246 | NanReturnUndefined(); 1247 | } 1248 | 1249 | if (args.Length() < 2) 1250 | { 1251 | NanThrowError("Not enough args."); 1252 | NanReturnUndefined(); 1253 | } 1254 | 1255 | Local buffer = args[0].As(); 1256 | if (!node::Buffer::HasInstance(buffer)) 1257 | { 1258 | NanThrowError("Expected a Buffer in args[0]."); 1259 | NanReturnUndefined(); 1260 | } 1261 | size_t length = node::Buffer::Length(buffer); 1262 | char *data = node::Buffer::Data(buffer); 1263 | 1264 | int rsaLength = RSA_size(obj->rsa); 1265 | Local result = NanNewBufferHandle(rsaLength); 1266 | 1267 | if (!args[1]->IsInt32()) 1268 | { 1269 | NanThrowError("Expected a 32-bit integer in args[1]."); 1270 | NanReturnUndefined(); 1271 | } 1272 | int padding = args[1]->Uint32Value(); 1273 | 1274 | int ret = RSA_public_encrypt(length, (unsigned char *)data, 1275 | (unsigned char *)node::Buffer::Data(result), 1276 | obj->rsa, padding); 1277 | 1278 | if (ret < 0) 1279 | { 1280 | scheduleSslException(); 1281 | NanReturnUndefined(); 1282 | } 1283 | 1284 | NanReturnValue(result); 1285 | } 1286 | 1287 | /** 1288 | * Sets the underlying RSA object to correspond to the given 1289 | * private key (a Buffer of PEM format data). This throws an 1290 | * exception if the underlying RSA had previously been set. 1291 | */ 1292 | NAN_METHOD(RsaWrap::SetPrivateKeyPem) 1293 | { 1294 | NanScope(); 1295 | bool ok = true; 1296 | 1297 | if (args.Length() < 1) 1298 | { 1299 | NanThrowError("Missing args[0]."); 1300 | NanReturnUndefined(); 1301 | } 1302 | 1303 | RsaWrap *obj = ObjectWrap::Unwrap(args.Holder()); 1304 | obj = expectUnset(obj); 1305 | ok &= (obj != NULL); 1306 | 1307 | BIO *bio = NULL; 1308 | if (ok) 1309 | { 1310 | bio = getArg0Bio(args[0].As()); 1311 | ok &= (bio != NULL); 1312 | } 1313 | 1314 | Local buf = args[1].As(); 1315 | char *password = NULL; 1316 | if (ok && (args.Length() >= 2)) 1317 | { 1318 | password = copyBufferToCharStar(buf); 1319 | if (password == NULL) 1320 | { 1321 | NanThrowError("Expected a Buffer in args[1]."); 1322 | } 1323 | ok &= (password != NULL); 1324 | } 1325 | 1326 | if (ok) 1327 | { 1328 | obj->rsa = PEM_read_bio_RSAPrivateKey(bio, NULL, 0, password); 1329 | if (obj->rsa == NULL) 1330 | { 1331 | scheduleSslException(); 1332 | } 1333 | } 1334 | 1335 | if (bio != NULL) 1336 | { 1337 | BIO_vfree(bio); 1338 | } 1339 | if (password != NULL) 1340 | { 1341 | free(password); 1342 | }; 1343 | NanReturnUndefined(); 1344 | } 1345 | 1346 | /** 1347 | * Sets the underlying RSA object to correspond to the given 1348 | * public key (a Buffer of PEM format data). This throws an 1349 | * exception if the underlying RSA had previously been set. 1350 | */ 1351 | NAN_METHOD(RsaWrap::SetPublicKeyPem) 1352 | { 1353 | NanScope(); 1354 | 1355 | RsaWrap *obj = ObjectWrap::Unwrap(args.Holder()); 1356 | obj = expectUnset(obj); 1357 | if (obj == NULL) 1358 | { 1359 | NanReturnUndefined(); 1360 | } 1361 | 1362 | if (args.Length() < 1) 1363 | { 1364 | NanThrowError("Missing args[0]."); 1365 | NanReturnUndefined(); 1366 | } 1367 | 1368 | BIO *bio = getArg0Bio(args[0].As()); 1369 | if (bio == NULL) 1370 | { 1371 | NanReturnUndefined(); 1372 | } 1373 | 1374 | obj->rsa = PEM_read_bio_RSA_PUBKEY(bio, NULL, NULL, NULL); 1375 | 1376 | if (obj->rsa == NULL) 1377 | { 1378 | scheduleSslException(); 1379 | } 1380 | 1381 | BIO_vfree(bio); 1382 | NanReturnUndefined(); 1383 | } 1384 | 1385 | /** 1386 | * Sign the given hash data. First argument indicates what kind of hash 1387 | * was performed. Returns a Buffer object. 1388 | */ 1389 | NAN_METHOD(RsaWrap::Sign) 1390 | { 1391 | NanScope(); 1392 | 1393 | RsaWrap *obj = ObjectWrap::Unwrap(args.Holder()); 1394 | obj = expectPrivateKey(obj); 1395 | if (obj == NULL) 1396 | { 1397 | NanReturnUndefined(); 1398 | } 1399 | 1400 | if (args.Length() < 2) 1401 | { 1402 | NanThrowError("Not enough args."); 1403 | NanReturnUndefined(); 1404 | } 1405 | 1406 | if (!args[0]->IsInt32()) 1407 | { 1408 | NanThrowError("Expected a 32-bit integer in args[0]."); 1409 | NanReturnUndefined(); 1410 | } 1411 | int nid = args[0]->Uint32Value(); 1412 | 1413 | Local buffer = args[1].As(); 1414 | if (!node::Buffer::HasInstance(buffer)) 1415 | { 1416 | NanThrowError("Expected a Buffer in args[1]."); 1417 | NanReturnUndefined(); 1418 | } 1419 | size_t dataLength = node::Buffer::Length(buffer); 1420 | char *data = node::Buffer::Data(buffer); 1421 | if (data == NULL) 1422 | { 1423 | NanReturnUndefined(); 1424 | } 1425 | 1426 | unsigned int rsaSize = (unsigned int)RSA_size(obj->rsa); 1427 | unsigned int sigLength = rsaSize; 1428 | Local result = NanNewBufferHandle(sigLength); 1429 | 1430 | int ret = RSA_sign(nid, (unsigned char *)data, dataLength, 1431 | (unsigned char *)node::Buffer::Data(result), 1432 | &sigLength, obj->rsa); 1433 | 1434 | if (ret == 0) 1435 | { 1436 | // TODO: Will this leak the result buffer? Is it going to be gc'ed? 1437 | scheduleSslException(); 1438 | NanReturnUndefined(); 1439 | } 1440 | 1441 | if (rsaSize != sigLength) 1442 | { 1443 | // Sanity check. Shouldn't ever happen in practice. 1444 | NanThrowError("Shouldn't happen."); 1445 | } 1446 | 1447 | NanReturnValue(result); 1448 | } 1449 | 1450 | /** 1451 | * Verify the signature on the given hash data. First argument indicates 1452 | * what kind of hash was performed. Throws an exception if the signature 1453 | * did not verify. 1454 | */ 1455 | NAN_METHOD(RsaWrap::Verify) 1456 | { 1457 | NanScope(); 1458 | 1459 | RsaWrap *obj = ObjectWrap::Unwrap(args.Holder()); 1460 | obj = expectSet(obj); 1461 | if (obj == NULL) 1462 | { 1463 | NanReturnUndefined(); 1464 | } 1465 | 1466 | if (args.Length() < 3) 1467 | { 1468 | NanThrowError("Not enough args."); 1469 | NanReturnUndefined(); 1470 | } 1471 | 1472 | if (!args[0]->IsInt32()) 1473 | { 1474 | NanThrowError("Expected a 32-bit integer in args[0]."); 1475 | NanReturnUndefined(); 1476 | } 1477 | int nid = args[0]->Uint32Value(); 1478 | 1479 | Local buffer = args[1].As(); 1480 | if (!node::Buffer::HasInstance(buffer)) 1481 | { 1482 | NanThrowError("Expected a Buffer in args[1]."); 1483 | NanReturnUndefined(); 1484 | } 1485 | size_t dataLength = node::Buffer::Length(buffer); 1486 | char *data = node::Buffer::Data(buffer); 1487 | if (data == NULL) 1488 | { 1489 | NanReturnUndefined(); 1490 | } 1491 | 1492 | Local sigBuffer = args[2].As(); 1493 | if (!node::Buffer::HasInstance(sigBuffer)) 1494 | { 1495 | NanThrowError("Expected a Buffer in args[2]."); 1496 | NanReturnUndefined(); 1497 | } 1498 | size_t sigLength = node::Buffer::Length(sigBuffer); 1499 | char *sig = node::Buffer::Data(sigBuffer); 1500 | if (sig == NULL) 1501 | { 1502 | NanReturnUndefined(); 1503 | } 1504 | 1505 | int ret = RSA_verify(nid, (unsigned char *)data, dataLength, 1506 | (unsigned char *)sig, sigLength, obj->rsa); 1507 | if (ret == 0) 1508 | { 1509 | // Something went wrong; investigate! 1510 | unsigned long err = ERR_peek_error(); 1511 | int lib = ERR_GET_LIB(err); 1512 | int reason = ERR_GET_REASON(err); 1513 | if ((lib == ERR_LIB_RSA) && (reason == RSA_R_BAD_SIGNATURE)) 1514 | { 1515 | // This just means that the signature didn't match 1516 | // (as opposed to, say, a more dire failure in the library 1517 | // warranting an exception throw). 1518 | ERR_get_error(); // Consume the error (get it off the err stack). 1519 | NanReturnValue(NanFalse()); 1520 | } 1521 | scheduleSslException(); 1522 | NanReturnUndefined(); 1523 | } 1524 | 1525 | NanReturnValue(NanTrue()); 1526 | } 1527 | 1528 | /** 1529 | * Add PSS padding to a digest. First argument is digest algorithm ID, 1530 | * second is the digest, third is the salt length. 1531 | */ 1532 | NAN_METHOD(RsaWrap::AddPSSPadding) 1533 | { 1534 | NanScope(); 1535 | 1536 | RsaWrap *obj = ObjectWrap::Unwrap(args.Holder()); 1537 | obj = expectSet(obj); 1538 | if (obj == NULL) 1539 | { 1540 | NanReturnUndefined(); 1541 | } 1542 | 1543 | if (args.Length() < 3) 1544 | { 1545 | NanThrowError("Not enough args."); 1546 | NanReturnUndefined(); 1547 | } 1548 | 1549 | if (!args[0]->IsInt32()) 1550 | { 1551 | NanThrowError("Expected a 32-bit integer in args[0]."); 1552 | NanReturnUndefined(); 1553 | } 1554 | int nid = args[0]->Uint32Value(); 1555 | const EVP_MD *Hash = EVP_get_digestbynid(nid); 1556 | if (Hash == NULL) 1557 | { 1558 | NanReturnUndefined(); 1559 | } 1560 | 1561 | Local buffer = args[1].As(); 1562 | if (!node::Buffer::HasInstance(buffer)) 1563 | { 1564 | NanThrowError("Expected a Buffer in args[1]."); 1565 | NanReturnUndefined(); 1566 | } 1567 | size_t mHashLength = node::Buffer::Length(buffer); 1568 | char *mHash = node::Buffer::Data(buffer); 1569 | if (mHash == NULL) 1570 | { 1571 | NanReturnUndefined(); 1572 | } 1573 | if (mHashLength != (size_t)EVP_MD_size(Hash)) 1574 | { 1575 | NanThrowError("Incorrect hash size."); 1576 | NanReturnUndefined(); 1577 | } 1578 | 1579 | if (!args[2]->IsInt32()) 1580 | { 1581 | NanThrowError("Expected a 32-bit integer in args[2]."); 1582 | NanReturnUndefined(); 1583 | } 1584 | int sLen = args[2]->Uint32Value(); 1585 | 1586 | unsigned int emLength = (unsigned int)RSA_size(obj->rsa); 1587 | Local EM = NanNewBufferHandle(emLength); 1588 | 1589 | int ret = RSA_padding_add_PKCS1_PSS(obj->rsa, 1590 | (unsigned char *)node::Buffer::Data(EM), 1591 | (unsigned char *)mHash, Hash, sLen); 1592 | if (ret == 0) 1593 | { 1594 | scheduleSslException(); 1595 | NanReturnUndefined(); 1596 | } 1597 | 1598 | NanReturnValue(EM); 1599 | } 1600 | 1601 | /** 1602 | * Verify a signature with PSS padding. First argument is digest algorithm ID, 1603 | * second is the digest, third is the padded digest, fourth is the salt length. 1604 | */ 1605 | NAN_METHOD(RsaWrap::VerifyPSSPadding) 1606 | { 1607 | NanScope(); 1608 | 1609 | RsaWrap *obj = ObjectWrap::Unwrap(args.Holder()); 1610 | obj = expectSet(obj); 1611 | if (obj == NULL) 1612 | { 1613 | NanReturnUndefined(); 1614 | } 1615 | 1616 | if (args.Length() < 4) 1617 | { 1618 | NanThrowError("Not enough args."); 1619 | NanReturnUndefined(); 1620 | } 1621 | 1622 | if (!args[0]->IsInt32()) 1623 | { 1624 | NanThrowError("Expected a 32-bit integer in args[0]."); 1625 | NanReturnUndefined(); 1626 | } 1627 | int nid = args[0]->Uint32Value(); 1628 | const EVP_MD *Hash = EVP_get_digestbynid(nid); 1629 | if (Hash == NULL) 1630 | { 1631 | NanReturnUndefined(); 1632 | } 1633 | 1634 | Local buffer = args[1].As(); 1635 | if (!node::Buffer::HasInstance(buffer)) 1636 | { 1637 | NanThrowError("Expected a Buffer in args[1]."); 1638 | NanReturnUndefined(); 1639 | } 1640 | size_t mHashLength = node::Buffer::Length(buffer); 1641 | char *mHash = node::Buffer::Data(buffer); 1642 | if (mHash == NULL) 1643 | { 1644 | NanReturnUndefined(); 1645 | } 1646 | if (mHashLength != (size_t)EVP_MD_size(Hash)) 1647 | { 1648 | NanThrowError("Incorrect hash size."); 1649 | NanReturnUndefined(); 1650 | } 1651 | 1652 | Local emBuffer = args[2].As(); 1653 | if (!node::Buffer::HasInstance(emBuffer)) 1654 | { 1655 | NanThrowError("Expected a Buffer in args[2]."); 1656 | NanReturnUndefined(); 1657 | } 1658 | if (node::Buffer::Length(emBuffer) != (size_t)RSA_size(obj->rsa)) 1659 | { 1660 | NanThrowError("Incorrect encoded message size."); 1661 | NanReturnUndefined(); 1662 | } 1663 | char *EM = node::Buffer::Data(emBuffer); 1664 | if (EM == NULL) 1665 | { 1666 | NanReturnUndefined(); 1667 | } 1668 | 1669 | if (!args[3]->IsInt32()) 1670 | { 1671 | NanThrowError("Expected a 32-bit integer in args[3]."); 1672 | NanReturnUndefined(); 1673 | } 1674 | int sLen = args[3]->Uint32Value(); 1675 | 1676 | int ret = RSA_verify_PKCS1_PSS(obj->rsa, 1677 | (unsigned char *)mHash, Hash, (unsigned char *)EM, sLen); 1678 | if (ret == 0) 1679 | { 1680 | // Something went wrong; investigate! 1681 | unsigned long err = ERR_peek_error(); 1682 | int lib = ERR_GET_LIB(err); 1683 | int reason = ERR_GET_REASON(err); 1684 | if ((lib == ERR_LIB_RSA) && (reason == RSA_R_BAD_SIGNATURE)) 1685 | { 1686 | // This just means that the signature didn't match 1687 | // (as opposed to, say, a more dire failure in the library 1688 | // warranting an exception throw). 1689 | ERR_get_error(); // Consume the error (get it off the err stack). 1690 | NanReturnValue(NanFalse()); 1691 | } 1692 | scheduleSslException(); 1693 | NanReturnUndefined(); 1694 | } 1695 | 1696 | NanReturnValue(NanTrue()); 1697 | } 1698 | 1699 | NAN_METHOD(RsaWrap::CreatePrivateKeyFromComponents) 1700 | { 1701 | RsaWrap *obj = ObjectWrap::Unwrap(args.Holder()); 1702 | obj = expectUnset(obj); 1703 | if (obj == NULL) 1704 | { 1705 | NanReturnUndefined(); 1706 | } 1707 | 1708 | if (args.Length() < 8) 1709 | { 1710 | NanThrowError("Not enough args."); 1711 | NanReturnUndefined(); 1712 | } 1713 | 1714 | obj->rsa = RSA_new(); 1715 | if (obj->rsa == NULL) 1716 | { 1717 | NanReturnUndefined(); 1718 | } 1719 | 1720 | BIGNUM *modulus = NULL; 1721 | BIGNUM *exponent = NULL; 1722 | BIGNUM *p = NULL; 1723 | BIGNUM *q = NULL; 1724 | BIGNUM *dp = NULL; 1725 | BIGNUM *dq = NULL; 1726 | BIGNUM *inverseQ = NULL; 1727 | BIGNUM *d = NULL; 1728 | 1729 | bool ok = true; 1730 | 1731 | modulus = getArgXBigNum(args[0].As()); 1732 | ok &= (modulus != NULL); 1733 | if (ok) 1734 | { 1735 | exponent = getArgXBigNum(args[1].As()); 1736 | ok &= (exponent != NULL); 1737 | } 1738 | if (ok) 1739 | { 1740 | p = getArgXBigNum(args[2].As()); 1741 | ok &= (p != NULL); 1742 | } 1743 | if (ok) 1744 | { 1745 | q = getArgXBigNum(args[3].As()); 1746 | ok &= (q != NULL); 1747 | } 1748 | if (ok) 1749 | { 1750 | dp = getArgXBigNum(args[4].As()); 1751 | ok &= (dp != NULL); 1752 | } 1753 | if (ok) 1754 | { 1755 | dq = getArgXBigNum(args[5].As()); 1756 | ok &= (dq != NULL); 1757 | } 1758 | if (ok) 1759 | { 1760 | inverseQ = getArgXBigNum(args[6].As()); 1761 | ok &= (inverseQ != NULL); 1762 | } 1763 | if (ok) 1764 | { 1765 | d = getArgXBigNum(args[7].As()); 1766 | ok &= (d != NULL); 1767 | } 1768 | 1769 | if (ok) 1770 | { 1771 | RSA_set0_key(obj->rsa, modulus, exponent, d); 1772 | RSA_set0_factors(obj->rsa, p, q); 1773 | RSA_set0_crt_params(obj->rsa, dp, dq, inverseQ); 1774 | } 1775 | else 1776 | { 1777 | if (modulus) 1778 | { 1779 | BN_free(modulus); 1780 | } 1781 | if (exponent) 1782 | { 1783 | BN_free(exponent); 1784 | } 1785 | if (p) 1786 | { 1787 | BN_free(p); 1788 | } 1789 | if (q) 1790 | { 1791 | BN_free(q); 1792 | } 1793 | if (dp) 1794 | { 1795 | BN_free(dp); 1796 | } 1797 | if (dq) 1798 | { 1799 | BN_free(dq); 1800 | } 1801 | if (inverseQ) 1802 | { 1803 | BN_free(inverseQ); 1804 | } 1805 | if (d) 1806 | { 1807 | BN_free(d); 1808 | } 1809 | } 1810 | 1811 | NanReturnUndefined(); 1812 | } 1813 | 1814 | NAN_METHOD(RsaWrap::CreatePublicKeyFromComponents) 1815 | { 1816 | RsaWrap *obj = ObjectWrap::Unwrap(args.Holder()); 1817 | obj = expectUnset(obj); 1818 | if (obj == NULL) 1819 | { 1820 | NanReturnUndefined(); 1821 | } 1822 | 1823 | if (args.Length() < 2) 1824 | { 1825 | NanThrowError("Not enough args."); 1826 | NanReturnUndefined(); 1827 | } 1828 | 1829 | obj->rsa = RSA_new(); 1830 | if (obj->rsa == NULL) 1831 | { 1832 | NanReturnUndefined(); 1833 | } 1834 | 1835 | BIGNUM *modulus = NULL; 1836 | BIGNUM *exponent = NULL; 1837 | 1838 | bool ok = true; 1839 | 1840 | modulus = getArgXBigNum(args[0].As()); 1841 | ok &= (modulus != NULL); 1842 | if (ok) 1843 | { 1844 | exponent = getArgXBigNum(args[1].As()); 1845 | ok &= (exponent != NULL); 1846 | } 1847 | 1848 | if (ok) 1849 | { 1850 | RSA_set0_key(obj->rsa, modulus, exponent, NULL); 1851 | } 1852 | else 1853 | { 1854 | if (modulus) 1855 | { 1856 | BN_free(modulus); 1857 | } 1858 | if (exponent) 1859 | { 1860 | BN_free(exponent); 1861 | } 1862 | } 1863 | 1864 | NanReturnUndefined(); 1865 | } 1866 | --------------------------------------------------------------------------------