|
702 | 702 | * @param {Array.<number>} b Bytes to crypt |
703 | 703 | * @param {Array.<number>} salt Salt bytes to use |
704 | 704 | * @param {number} rounds Number of rounds |
705 | | - * @param {function(Error, Array.<number>)=} callback Callback receiving the error, if any, and the resulting bytes. If |
| 705 | + * @param {function(Error, Array.<number>=)=} callback Callback receiving the error, if any, and the resulting bytes. If |
706 | 706 | * omitted, the operation will be performed synchronously. |
707 | | - * @returns {Array.<number>} Resulting bytes or if callback has been omitted, otherwise null |
| 707 | + * @returns {!Array.<number>|undefined} Resulting bytes if callback has been omitted, otherwise `undefined` |
708 | 708 | * @private |
709 | 709 | */ |
710 | 710 | function _crypt(b, salt, rounds, callback) { |
711 | | - var cdata = C_ORIG.slice(); |
712 | | - var clen = cdata.length; |
| 711 | + var cdata = C_ORIG.slice(), |
| 712 | + clen = cdata.length, |
| 713 | + err; |
713 | 714 |
|
714 | 715 | // Validate |
715 | 716 | if (rounds < 4 || rounds > 31) { |
716 | | - throw(new Error("Illegal number of rounds: "+rounds)); |
| 717 | + err = new Error("Illegal number of rounds: "+rounds); |
| 718 | + if (callback) { |
| 719 | + _nextTick(callback.bind(this, err)); |
| 720 | + return; |
| 721 | + } else throw err; |
717 | 722 | } |
718 | 723 | if (salt.length != BCRYPT_SALT_LEN) { |
719 | | - throw(new Error("Illegal salt length: "+salt.length+" != "+BCRYPT_SALT_LEN)); |
| 724 | + err = new Error("Illegal salt length: "+salt.length+" != "+BCRYPT_SALT_LEN); |
| 725 | + if (callback) { |
| 726 | + _nextTick(callback.bind(this, err)); |
| 727 | + return; |
| 728 | + } else throw err; |
720 | 729 | } |
721 | 730 | rounds = 1 << rounds; |
722 | 731 | var P = P_ORIG.slice(); |
|
728 | 737 |
|
729 | 738 | /** |
730 | 739 | * Calcualtes the next round. |
731 | | - * @returns {Array.<number>} Resulting array if callback has been omitted, otherwise null |
| 740 | + * @returns {Array.<number>|undefined} Resulting array if callback has been omitted, otherwise `undefined` |
732 | 741 | * @private |
733 | 742 | */ |
734 | 743 | function next() { |
|
757 | 766 | } |
758 | 767 | if (callback) { |
759 | 768 | callback(null, ret); |
760 | | - return null; |
| 769 | + return; |
761 | 770 | } else { |
762 | 771 | return ret; |
763 | 772 | } |
764 | 773 | } |
765 | 774 | if (callback) { |
766 | 775 | _nextTick(next); |
767 | 776 | } |
768 | | - return null; |
769 | 777 | } |
770 | 778 |
|
771 | 779 | // Async |
772 | 780 | if (typeof callback !== 'undefined') { |
773 | 781 | next(); |
774 | | - return null; |
| 782 | + |
775 | 783 | // Sync |
776 | 784 | } else { |
777 | 785 | var res; |
778 | 786 | while (true) { |
779 | | - if ((res = next()) !== null) { |
780 | | - return res; |
| 787 | + if ((res = next()) !== undefined) { |
| 788 | + return res || []; |
781 | 789 | } |
782 | 790 | } |
783 | 791 | } |
|
800 | 808 | * Internally hashes a string. |
801 | 809 | * @param {string} s String to hash |
802 | 810 | * @param {?string} salt Salt to use, actually never null |
803 | | - * @param {function(Error, ?string)=} callback Callback receiving the error, if any, and the resulting hash. If omitted, |
| 811 | + * @param {function(Error, string=)=} callback Callback receiving the error, if any, and the resulting hash. If omitted, |
804 | 812 | * hashing is perormed synchronously. |
805 | | - * @returns {?string} Resulting hash if callback has been omitted, else null |
| 813 | + * @returns {string|undefined} Resulting hash if callback has been omitted, otherwise `undefined` |
806 | 814 | * @private |
807 | 815 | */ |
808 | 816 | function _hash(s, salt, callback) { |
809 | | - |
| 817 | + var err; |
| 818 | + if (typeof s !== 'string' || typeof salt !== 'string') { |
| 819 | + err = new Error("Invalid string / salt: Not a string"); |
| 820 | + if (callback) { |
| 821 | + _nextTick(callback.bind(this, err)); |
| 822 | + return; |
| 823 | + } |
| 824 | + else throw err; |
| 825 | + } |
| 826 | + |
810 | 827 | // Validate the salt |
811 | 828 | var minor, offset; |
812 | | - if (salt.charAt(0) != '$' || salt.charAt(1) != '2') { |
813 | | - throw(new Error("Invalid salt version: "+salt.substring(0,2))); |
| 829 | + if (salt.charAt(0) !== '$' || salt.charAt(1) !== '2') { |
| 830 | + err = new Error("Invalid salt version: "+salt.substring(0,2)); |
| 831 | + if (callback) { |
| 832 | + _nextTick(callback.bind(this, err)); |
| 833 | + return; |
| 834 | + } |
| 835 | + else throw err; |
814 | 836 | } |
815 | | - if (salt.charAt(2) == '$') { |
| 837 | + if (salt.charAt(2) === '$') { |
816 | 838 | minor = String.fromCharCode(0); |
817 | 839 | offset = 3; |
818 | 840 | } else { |
819 | 841 | minor = salt.charAt(2); |
820 | 842 | if (minor != 'a' || salt.charAt(3) != '$') { |
821 | | - throw(new Error("Invalid salt revision: "+salt.substring(2,4))); |
| 843 | + err = new Error("Invalid salt revision: "+salt.substring(2,4)); |
| 844 | + if (callback) { |
| 845 | + _nextTick(callback.bind(this, err)); |
| 846 | + return; |
| 847 | + } else throw err; |
822 | 848 | } |
823 | 849 | offset = 4; |
824 | 850 | } |
825 | 851 |
|
826 | 852 | // Extract number of rounds |
827 | 853 | if (salt.charAt(offset + 2) > '$') { |
828 | | - throw(new Error("Missing salt rounds")); |
| 854 | + err = new Error("Missing salt rounds"); |
| 855 | + if (callback) { |
| 856 | + _nextTick(callback.bind(this, err)); |
| 857 | + return; |
| 858 | + } else throw err; |
829 | 859 | } |
830 | 860 | var r1 = parseInt(salt.substring(offset, offset + 1), 10) * 10; |
831 | 861 | var r2 = parseInt(salt.substring(offset + 1, offset + 2), 10); |
|
838 | 868 | saltb = base64_decode(real_salt, BCRYPT_SALT_LEN); |
839 | 869 |
|
840 | 870 | /** |
841 | | - * Finishs hashing. |
| 871 | + * Finishes hashing. |
842 | 872 | * @param {Array.<number>} bytes Byte array |
843 | 873 | * @returns {string} |
844 | 874 | * @private |
|
860 | 890 | if (typeof callback == 'undefined') { |
861 | 891 | return finish(_crypt(passwordb, saltb, rounds)); |
862 | 892 |
|
863 | | - // Async |
| 893 | + // Async |
864 | 894 | } else { |
865 | 895 | _crypt(passwordb, saltb, rounds, function(err, bytes) { |
866 | 896 | if (err) { |
|
869 | 899 | callback(null, finish(bytes)); |
870 | 900 | } |
871 | 901 | }); |
872 | | - return null; |
873 | 902 | } |
874 | 903 | } |
875 | 904 |
|
|
893 | 922 | } else if (typeof _getRandomValues === 'function') { |
894 | 923 | _getRandomValues(array); |
895 | 924 | } else { |
896 | | - throw(new Error("Failed to generate random values: Web Crypto API not available / no polyfill set")); |
| 925 | + throw(new Error("Failed to generate random values: Web Crypto API not available / polyfill not set through bcrypt.setRandomPolyfill")); |
897 | 926 | } |
898 | 927 | return Array.prototype.slice.call(array); |
899 | 928 | } |
|
907 | 936 | * @private |
908 | 937 | */ |
909 | 938 | function _gensalt(rounds) { |
910 | | - rounds = rounds || 10; |
| 939 | + rounds = rounds || GENSALT_DEFAULT_LOG2_ROUNDS; |
911 | 940 | if (rounds < 4 || rounds > 31) { |
912 | 941 | throw(new Error("Illegal number of rounds: "+rounds)); |
913 | 942 | } |
914 | 943 | var salt = []; |
915 | 944 | salt.push("$2a$"); |
916 | | - if (rounds < GENSALT_DEFAULT_LOG2_ROUNDS) salt.push("0"); |
| 945 | + if (rounds < 10) salt.push("0"); |
917 | 946 | salt.push(rounds.toString()); |
918 | 947 | salt.push('$'); |
919 | 948 | try { |
|
944 | 973 | * @expose |
945 | 974 | */ |
946 | 975 | bcrypt.genSaltSync = function(rounds, seed_length) { |
947 | | - if (!rounds) rounds = 10; |
| 976 | + if (typeof rounds === 'undefined') |
| 977 | + rounds = GENSALT_DEFAULT_LOG2_ROUNDS; |
| 978 | + else if (typeof rounds !== 'number') |
| 979 | + throw(new Error("Illegal argument types: "+(typeof rounds)+", "+(typeof seed_length))); |
948 | 980 | return _gensalt(rounds); |
949 | 981 | }; |
950 | 982 |
|
951 | 983 | /** |
952 | 984 | * Asynchronously generates a salt. |
953 | | - * @param {(number|function(Error, ?string))=} rounds Number of rounds to use, defaults to 10 if omitted |
954 | | - * @param {(number|function(Error, ?string))=} seed_length Not supported. |
| 985 | + * @param {(number|function(Error, string=))=} rounds Number of rounds to use, defaults to 10 if omitted |
| 986 | + * @param {(number|function(Error, string=))=} seed_length Not supported. |
955 | 987 | * @param {function(Error, ?string)=} callback Callback receiving the error, if any, and the resulting salt |
956 | 988 | * @expose |
957 | 989 | */ |
958 | 990 | bcrypt.genSalt = function(rounds, seed_length, callback) { |
959 | | - if (typeof seed_length == 'function') { |
| 991 | + if (typeof seed_length === 'function') { |
960 | 992 | callback = seed_length; |
961 | | - seed_length = -1; // Not supported. |
| 993 | + seed_length = undefined; // Not supported. |
962 | 994 | } |
963 | 995 | var rnd; // Hello closure |
964 | | - if (typeof rounds == 'function') { |
| 996 | + if (typeof rounds === 'function') { |
965 | 997 | callback = rounds; |
966 | 998 | rnd = GENSALT_DEFAULT_LOG2_ROUNDS; |
967 | | - } else { |
968 | | - rnd = parseInt(rounds, 10); |
969 | 999 | } |
970 | | - if (typeof callback != 'function') { |
971 | | - throw(new Error("Illegal or missing 'callback': "+callback)); |
| 1000 | + if (typeof callback !== 'function') |
| 1001 | + throw(new Error("Illegal callback: "+callback)); |
| 1002 | + if (typeof rounds !== 'number') { |
| 1003 | + _nextTick(callback.bind(this, new Error("Illegal argument types: "+(typeof rounds)))); |
| 1004 | + return; |
972 | 1005 | } |
973 | 1006 | _nextTick(function() { // Pretty thin, but salting is fast enough |
974 | 1007 | try { |
975 | | - var res = bcrypt.genSaltSync(rnd); |
976 | | - callback(null, res); |
| 1008 | + callback(null, bcrypt.genSaltSync(rnd)); |
977 | 1009 | } catch(err) { |
978 | | - callback(err, null); |
| 1010 | + callback(err); |
979 | 1011 | } |
980 | 1012 | }); |
981 | 1013 | }; |
|
984 | 1016 | * Synchronously generates a hash for the given string. |
985 | 1017 | * @param {string} s String to hash |
986 | 1018 | * @param {(number|string)=} salt Salt length to generate or salt to use, default to 10 |
987 | | - * @returns {?string} Resulting hash, actually never null |
| 1019 | + * @returns {string} Resulting hash |
988 | 1020 | * @expose |
989 | 1021 | */ |
990 | 1022 | bcrypt.hashSync = function(s, salt) { |
991 | | - if (!salt) salt = GENSALT_DEFAULT_LOG2_ROUNDS; |
992 | | - if (typeof salt === 'number') { |
| 1023 | + if (typeof salt === 'undefined') |
| 1024 | + salt = GENSALT_DEFAULT_LOG2_ROUNDS; |
| 1025 | + if (typeof salt === 'number') |
993 | 1026 | salt = bcrypt.genSaltSync(salt); |
994 | | - } |
| 1027 | + if (typeof s !== 'string' || typeof salt !== 'string') |
| 1028 | + throw new Error("Illegal argument types: "+(typeof s)+', '+(typeof salt)); |
995 | 1029 | return _hash(s, salt); |
996 | 1030 | }; |
997 | 1031 |
|
998 | 1032 | /** |
999 | 1033 | * Asynchronously generates a hash for the given string. |
1000 | 1034 | * @param {string} s String to hash |
1001 | 1035 | * @param {number|string} salt Salt length to generate or salt to use |
1002 | | - * @param {function(Error, ?string)} callback Callback receiving the error, if any, and the resulting hash |
| 1036 | + * @param {function(Error, string=)} callback Callback receiving the error, if any, and the resulting hash |
1003 | 1037 | * @expose |
1004 | 1038 | */ |
1005 | 1039 | bcrypt.hash = function(s, salt, callback) { |
1006 | | - if (typeof callback !== 'function') { |
1007 | | - throw(new Error("Illegal 'callback': "+callback)); |
1008 | | - } |
1009 | | - if (typeof salt === 'number') { |
| 1040 | + if (typeof callback !== 'function') |
| 1041 | + throw(new Error("Illegal callback: "+callback)); |
| 1042 | + if (typeof s === 'string' && typeof salt === 'number') { |
1010 | 1043 | bcrypt.genSalt(salt, function(err, salt) { |
1011 | 1044 | _hash(s, salt, callback); |
1012 | 1045 | }); |
1013 | | - } else { |
| 1046 | + } else if (typeof s === 'string' && typeof salt === 'string') { |
1014 | 1047 | _hash(s, salt, callback); |
| 1048 | + } else { |
| 1049 | + _nextTick(callback.bind(this, new Error("Illegal argument types: "+(typeof s)+', '+(typeof salt)))); |
1015 | 1050 | } |
1016 | 1051 | }; |
1017 | 1052 |
|
|
1024 | 1059 | * @expose |
1025 | 1060 | */ |
1026 | 1061 | bcrypt.compareSync = function(s, hash) { |
1027 | | - if(typeof s !== "string" || typeof hash !== "string") { |
| 1062 | + if (typeof s !== "string" || typeof hash !== "string") |
1028 | 1063 | throw(new Error("Illegal argument types: "+(typeof s)+', '+(typeof hash))); |
1029 | | - } |
1030 | 1064 | if (hash.length !== 60) return false; |
1031 | 1065 | var comp = bcrypt.hashSync(s, hash.substr(0, hash.length-31)); |
1032 | | - var same = comp.length == hash.length; |
| 1066 | + var same = comp.length === hash.length; |
1033 | 1067 | var max_length = (comp.length < hash.length) ? comp.length : hash.length; |
1034 | 1068 |
|
1035 | 1069 | // to prevent timing attacks, should check entire string |
|
1051 | 1085 | * @expose |
1052 | 1086 | */ |
1053 | 1087 | bcrypt.compare = function(s, hash, callback) { |
1054 | | - if (typeof callback !== 'function') { |
1055 | | - throw(new Error("Illegal 'callback': "+callback)); |
| 1088 | + if (typeof callback !== 'function') |
| 1089 | + throw(new Error("Illegal callback: "+callback)); |
| 1090 | + if (typeof s !== "string" || typeof hash !== "string") { |
| 1091 | + _nextTick(callback.bind(this, new Error("Illegal argument types: "+(typeof s)+', '+(typeof hash)))); |
| 1092 | + return; |
1056 | 1093 | } |
1057 | 1094 | bcrypt.hash(s, hash.substr(0, 29), function(err, comp) { |
1058 | 1095 | callback(err, hash === comp); |
|
1067 | 1104 | * @expose |
1068 | 1105 | */ |
1069 | 1106 | bcrypt.getRounds = function(hash) { |
1070 | | - if(typeof hash !== "string") { |
1071 | | - throw(new Error("Illegal type of 'hash': "+(typeof hash))); |
1072 | | - } |
| 1107 | + if (typeof hash !== "string") |
| 1108 | + throw(new Error("Illegal argument types: "+(typeof hash))); |
1073 | 1109 | return parseInt(hash.split("$")[2], 10); |
1074 | 1110 | }; |
1075 | 1111 |
|
1076 | 1112 | /** |
1077 | | - * Gets the salt portion from a hash. |
| 1113 | + * Gets the salt portion from a hash. Does not validate the hash. |
1078 | 1114 | * @param {string} hash Hash to extract the salt from |
1079 | | - * @returns {string} Extracted salt part portion |
| 1115 | + * @returns {string} Extracted salt part |
1080 | 1116 | * @throws {Error} If `hash` is not a string or otherwise invalid |
1081 | 1117 | * @expose |
1082 | 1118 | */ |
1083 | 1119 | bcrypt.getSalt = function(hash) { |
1084 | | - if (typeof hash !== 'string') { |
1085 | | - throw(new Error("Illegal type of 'hash': "+(typeof hash))); |
1086 | | - } |
1087 | | - if (hash.length !== 60) { |
| 1120 | + if (typeof hash !== 'string') |
| 1121 | + throw(new Error("Illegal argument types: "+(typeof hash))); |
| 1122 | + if (hash.length !== 60) |
1088 | 1123 | throw(new Error("Illegal hash length: "+hash.length+" != 60")); |
1089 | | - } |
1090 | 1124 | return hash.substring(0, 29); |
1091 | 1125 | }; |
1092 | 1126 |
|
|
0 commit comments