1717 along with this program. If not, see <http://www.gnu.org/licenses/>.
1818*/
1919
20- var crypto = require ( 'crypto-browserify' )
20+ // Allow optionally using crypto-browserify for legacy/insecure behavior during testing.
21+ let cryptoLib : any ;
22+ if ( process . env . USE_CRYPTO_BROWSERIFY === 'true' ) {
23+ // Use require so bundlers aren't forced to include this in production builds
24+ // and to allow runtime selection in dev/test scenarios.
25+ // eslint-disable-next-line @typescript-eslint/no-var-requires
26+ cryptoLib = require ( 'crypto-browserify' ) ;
27+ } else {
28+ // eslint-disable-next-line @typescript-eslint/no-var-requires
29+ cryptoLib = require ( 'crypto' ) ;
30+ }
2131
2232import Logger from "../middleware/logger" ;
2333
34+ // Provide an optional legacy shim that emulates the deprecated OpenSSL-style
35+ // `createCipher(algorithm, password)` / `createDecipher(algorithm, password)`
36+ // by deriving a key+iv using the EVP_BytesToKey MD5 scheme. This is insecure
37+ // but useful for intentionally testing detectors that look for legacy crypto use.
38+ function evpBytesToKey ( password : string , keyLen : number , ivLen : number ) {
39+ const passwordBuf = Buffer . from ( String ( password ) , 'binary' ) ;
40+ let m = Buffer . alloc ( 0 ) ;
41+ let i = 0 ;
42+ let md5 : Buffer = Buffer . alloc ( 0 ) ;
43+ while ( m . length < ( keyLen + ivLen ) ) {
44+ const hash = cryptoLib . createHash ( 'md5' ) ;
45+ if ( i === 0 ) {
46+ hash . update ( passwordBuf ) ;
47+ } else {
48+ hash . update ( Buffer . concat ( [ md5 , passwordBuf ] ) ) ;
49+ }
50+ md5 = hash . digest ( ) ;
51+ m = Buffer . concat ( [ m , md5 ] ) ;
52+ i ++ ;
53+ }
54+ return {
55+ key : m . slice ( 0 , keyLen ) ,
56+ iv : m . slice ( keyLen , keyLen + ivLen ) ,
57+ } ;
58+ }
59+
60+ function createLegacyCipher ( algorithm : string , password : string ) {
61+ // Only implemented for AES-*-CTR/GCM/CBC families used in this project.
62+ // For `aes-256-ctr` key=32 iv=16.
63+ const keyLen = algorithm . includes ( '256' ) ? 32 : 16 ;
64+ const ivLen = 16 ;
65+ const derived = evpBytesToKey ( password , keyLen , ivLen ) ;
66+ return cryptoLib . createCipheriv ( algorithm , derived . key , derived . iv ) ;
67+ }
68+
69+ function createLegacyDecipher ( algorithm : string , password : string ) {
70+ const keyLen = algorithm . includes ( '256' ) ? 32 : 16 ;
71+ const ivLen = 16 ;
72+ const derived = evpBytesToKey ( password , keyLen , ivLen ) ;
73+ return cryptoLib . createDecipheriv ( algorithm , derived . key , derived . iv ) ;
74+ }
75+
2476export abstract class EncryptUtils {
2577
2678 static jwtSecret = process . env . JWT_SECRET || "your-very-long-and-random-secret-key" ;
@@ -34,29 +86,59 @@ export abstract class EncryptUtils {
3486
3587 public static cryptPassword ( password : String ) : String {
3688 //process.stdout.write("Encrypting password: " + password);
37- var cipher = crypto . createCipher ( this . algorithm , this . encryptionKey ) ;
38- var mystr = cipher . update ( password , 'utf8' , 'hex' ) ;
89+ const useLegacy = process . env . USE_LEGACY_CIPHER === 'true' ;
90+ if ( useLegacy ) {
91+ const cipher = createLegacyCipher ( this . algorithm , this . encryptionKey ) ;
92+ let mystr = cipher . update ( String ( password ) , 'utf8' , 'hex' ) ;
93+ mystr += cipher . final ( 'hex' ) ;
94+ process . stdout . write ( "Encrypted password:" + mystr ) ;
95+ return mystr ;
96+ }
97+ const key = cryptoLib . createHash ( 'sha256' ) . update ( String ( this . encryptionKey ) ) . digest ( ) ;
98+ const iv = cryptoLib . randomBytes ( 16 ) ;
99+ const cipher = cryptoLib . createCipheriv ( this . algorithm , key , iv ) ;
100+ let mystr = cipher . update ( String ( password ) , 'utf8' , 'hex' ) ;
39101 mystr += cipher . final ( 'hex' ) ;
40- process . stdout . write ( "Encrypted password:" + mystr ) ;
41- return mystr ;
102+ const payload = iv . toString ( 'hex' ) + ':' + mystr ;
103+ process . stdout . write ( "Encrypted password:" + payload ) ;
104+ return payload ;
42105 }
43106
44107 public static decryptPassword ( hashPassword : String ) : String {
45108 process . stdout . write ( "Decrypting password: " + hashPassword ) ;
46- var cipher = crypto . createDecipher ( this . algorithm , this . encryptionKey ) ;
47- var mystr = cipher . update ( hashPassword , 'hex' , 'utf8' ) ;
48- mystr += cipher . final ( 'utf8' ) ;
109+ const useLegacy = process . env . USE_LEGACY_CIPHER === 'true' ;
110+ const parts = String ( hashPassword ) . split ( ':' ) ;
111+ if ( parts . length !== 2 ) {
112+ if ( useLegacy ) {
113+ // Attempt legacy-style decipher (no IV prefix)
114+ try {
115+ const decipher = createLegacyDecipher ( this . algorithm , this . encryptionKey ) ;
116+ let mystr = decipher . update ( String ( hashPassword ) , 'hex' , 'utf8' ) ;
117+ mystr += decipher . final ( 'utf8' ) ;
118+ process . stdout . write ( "Decrypted password:" + mystr ) ;
119+ return mystr ;
120+ } catch ( e ) {
121+ return String ( hashPassword ) ;
122+ }
123+ }
124+ // Not in iv:encrypted format - return as-is or throw
125+ return String ( hashPassword ) ;
126+ }
127+ const iv = Buffer . from ( parts [ 0 ] , 'hex' ) ;
128+ const encrypted = parts [ 1 ] ;
129+ const key = cryptoLib . createHash ( 'sha256' ) . update ( String ( this . encryptionKey ) ) . digest ( ) ;
130+ const decipher = cryptoLib . createDecipheriv ( this . algorithm , key , iv ) ;
131+ let mystr = decipher . update ( encrypted , 'hex' , 'utf8' ) ;
132+ mystr += decipher . final ( 'utf8' ) ;
49133 process . stdout . write ( "Decrypted password:" + mystr ) ;
50134 return mystr ;
51135 }
52136
53137 public static comparePassword ( password : String , hashPassword : String ) : Boolean {
54138 //process.stdout.write("Encrypted password: " + hashPassword);
55- var cipher = crypto . createDecipher ( this . algorithm , this . encryptionKey ) ;
56- var mystr = cipher . update ( hashPassword , 'hex' , 'utf8' ) ;
57- mystr += cipher . final ( 'utf8' ) ;
58- process . stdout . write ( "Comparing passwords: " + mystr + " = " + password ) ;
59- return ( password == mystr ) ;
139+ const plain = this . decryptPassword ( hashPassword ) ;
140+ process . stdout . write ( "Comparing passwords: " + plain + " = " + password ) ;
141+ return ( String ( password ) == String ( plain ) ) ;
60142 }
61143
62144}
0 commit comments