Skip to content

Commit 7fbb640

Browse files
committed
PBKDF2: Always use base64 in native code
1 parent 3eab42a commit 7fbb640

10 files changed

Lines changed: 71 additions & 96 deletions

File tree

android/src/main/java/com/pedrouid/crypto/RCTAes.java

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -96,24 +96,13 @@ public void randomKey(Integer length, Promise promise) {
9696
byte[] key = new byte[length];
9797
SecureRandom rand = new SecureRandom();
9898
rand.nextBytes(key);
99-
String keyHex = bytesToHex(key);
99+
String keyHex = Util.bytesToHex(key);
100100
promise.resolve(keyHex);
101101
} catch (Exception e) {
102102
promise.reject("-1", e.getMessage());
103103
}
104104
}
105105

106-
public static String bytesToHex(byte[] bytes) {
107-
final char[] hexArray = "0123456789abcdef".toCharArray();
108-
char[] hexChars = new char[bytes.length * 2];
109-
for ( int j = 0; j < bytes.length; j++ ) {
110-
int v = bytes[j] & 0xFF;
111-
hexChars[j * 2] = hexArray[v >>> 4];
112-
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
113-
}
114-
return new String(hexChars);
115-
}
116-
117106
final static IvParameterSpec emptyIvSpec = new IvParameterSpec(new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
118107

119108
private static String encrypt(String textBase64, String hexKey, String hexIv) throws Exception {

android/src/main/java/com/pedrouid/crypto/RCTHmac.java

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -69,23 +69,12 @@ public void hmac256(String data, String pwd, Promise promise) {
6969
}
7070
}
7171

72-
public static String bytesToHex(byte[] bytes) {
73-
final char[] hexArray = "0123456789abcdef".toCharArray();
74-
char[] hexChars = new char[bytes.length * 2];
75-
for ( int j = 0; j < bytes.length; j++ ) {
76-
int v = bytes[j] & 0xFF;
77-
hexChars[j * 2] = hexArray[v >>> 4];
78-
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
79-
}
80-
return new String(hexChars);
81-
}
82-
8372
private static String hmac256(String text, String key) throws NoSuchAlgorithmException, InvalidKeyException {
8473
byte[] contentData = Hex.decode(text);
8574
byte[] akHexData = Hex.decode(key);
8675
Mac sha256_HMAC = Mac.getInstance(HMAC_SHA_256);
8776
SecretKey secret_key = new SecretKeySpec(akHexData, HMAC_SHA_256);
8877
sha256_HMAC.init(secret_key);
89-
return bytesToHex(sha256_HMAC.doFinal(contentData));
78+
return Util.bytesToHex(sha256_HMAC.doFinal(contentData));
9079
}
9180
}
Lines changed: 21 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,25 @@
11
package com.pedrouid.crypto;
22

3-
import android.widget.Toast;
4-
5-
import java.io.IOException;
6-
import java.security.SecureRandom;
7-
import java.util.HashMap;
8-
import java.util.Map;
9-
10-
import java.util.UUID;
11-
12-
import java.security.MessageDigest;
13-
import java.security.NoSuchAlgorithmException;
14-
import java.security.spec.InvalidKeySpecException;
15-
import java.security.InvalidKeyException;
16-
17-
import java.nio.charset.StandardCharsets;
3+
import android.util.Base64;
184

19-
import javax.crypto.Cipher;
20-
import javax.crypto.SecretKey;
21-
import javax.crypto.spec.SecretKeySpec;
22-
import javax.crypto.spec.IvParameterSpec;
23-
import javax.crypto.spec.PBEKeySpec;
24-
import javax.crypto.SecretKeyFactory;
25-
import javax.crypto.Mac;
5+
import com.facebook.react.bridge.Promise;
6+
import com.facebook.react.bridge.ReactApplicationContext;
7+
import com.facebook.react.bridge.ReactContextBaseJavaModule;
8+
import com.facebook.react.bridge.ReactMethod;
269

2710
import org.spongycastle.crypto.ExtendedDigest;
11+
import org.spongycastle.crypto.PBEParametersGenerator;
2812
import org.spongycastle.crypto.digests.SHA1Digest;
2913
import org.spongycastle.crypto.digests.SHA224Digest;
3014
import org.spongycastle.crypto.digests.SHA256Digest;
3115
import org.spongycastle.crypto.digests.SHA384Digest;
32-
import org.spongycastle.crypto.digests.SHA384Digest;
3316
import org.spongycastle.crypto.digests.SHA512Digest;
3417
import org.spongycastle.crypto.generators.PKCS5S2ParametersGenerator;
35-
import org.spongycastle.crypto.PBEParametersGenerator;
3618
import org.spongycastle.crypto.params.KeyParameter;
37-
import org.spongycastle.util.encoders.Hex;
3819

39-
import android.util.Base64;
40-
41-
import com.facebook.react.bridge.NativeModule;
42-
import com.facebook.react.bridge.ReactApplicationContext;
43-
import com.facebook.react.bridge.Promise;
44-
import com.facebook.react.bridge.ReactContext;
45-
import com.facebook.react.bridge.ReactContextBaseJavaModule;
46-
import com.facebook.react.bridge.ReactMethod;
47-
import com.facebook.react.bridge.Callback;
20+
import java.security.NoSuchAlgorithmException;
21+
import java.util.HashMap;
22+
import java.util.Map;
4823

4924
public class RCTPbkdf2 extends ReactContextBaseJavaModule {
5025

@@ -58,27 +33,18 @@ public String getName() {
5833
}
5934

6035
@ReactMethod
61-
public void hash(String pwd, String saltBase64, Integer iterations, Integer keyLen, String hash, Promise promise) {
36+
public void hash(String pwdBase64, String saltBase64, Integer iterations, Integer keyLen, String hash, Promise promise) {
6237
try {
63-
String strs = pbkdf2(pwd, saltBase64, iterations, keyLen, hash);
64-
promise.resolve(strs);
38+
byte[] pwdBytes = Base64.decode(pwdBase64, Base64.DEFAULT);
39+
byte[] saltBytes = Base64.decode(saltBase64, Base64.DEFAULT);
40+
byte[] digest = pbkdf2(pwdBytes, saltBytes, iterations, keyLen, hash);
41+
promise.resolve(Base64.encodeToString(digest, Base64.NO_WRAP));
6542
} catch (Exception e) {
6643
promise.reject("-1", e.getMessage());
6744
}
6845
}
6946

70-
public static String bytesToHex(byte[] bytes) {
71-
final char[] hexArray = "0123456789abcdef".toCharArray();
72-
char[] hexChars = new char[bytes.length * 2];
73-
for ( int j = 0; j < bytes.length; j++ ) {
74-
int v = bytes[j] & 0xFF;
75-
hexChars[j * 2] = hexArray[v >>> 4];
76-
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
77-
}
78-
return new String(hexChars);
79-
}
80-
81-
private static String pbkdf2(String pwd, String salt, Integer iterations, Integer keyLen, String hash) throws NoSuchAlgorithmException, InvalidKeySpecException {
47+
private static byte[] pbkdf2(byte[] pwd, byte[] salt, Integer iterations, Integer keyLen, String hash) throws NullPointerException, NoSuchAlgorithmException {
8248
Map<String, ExtendedDigest> algMap = new HashMap<String, ExtendedDigest>();
8349
algMap.put("SHA1", new SHA1Digest());
8450
algMap.put("SHA224", new SHA224Digest());
@@ -87,10 +53,12 @@ private static String pbkdf2(String pwd, String salt, Integer iterations, Intege
8753
algMap.put("SHA512", new SHA512Digest());
8854
ExtendedDigest alg = algMap.get(hash);
8955

56+
if (alg == null) {
57+
throw new NoSuchAlgorithmException("Specified hash algorithm is not supported");
58+
}
59+
9060
PBEParametersGenerator gen = new PKCS5S2ParametersGenerator(alg);
91-
byte[] saltBytes = Base64.decode(salt, Base64.DEFAULT);
92-
gen.init(pwd.getBytes(StandardCharsets.UTF_8), saltBytes, iterations);
93-
byte[] key = ((KeyParameter) gen.generateDerivedParameters(keyLen * 8)).getKey();
94-
return bytesToHex(key);
61+
gen.init(pwd, salt, iterations);
62+
return ((KeyParameter) gen.generateDerivedParameters(keyLen * 8)).getKey();
9563
}
9664
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.pedrouid.crypto;
2+
3+
class Util {
4+
static String bytesToHex(byte[] bytes) {
5+
final char[] hexArray = "0123456789abcdef".toCharArray();
6+
char[] hexChars = new char[bytes.length * 2];
7+
for ( int j = 0; j < bytes.length; j++ ) {
8+
int v = bytes[j] & 0xFF;
9+
hexChars[j * 2] = hexArray[v >>> 4];
10+
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
11+
}
12+
return new String(hexChars);
13+
}
14+
}

index.d.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,11 @@ declare module "react-native-simple-crypto" {
3838

3939
export namespace PBKDF2 {
4040
export function hash(
41-
password: string,
42-
salt: ArrayBuffer,
41+
password: string | ArrayBuffer,
42+
salt: string | ArrayBuffer,
4343
iterations: number,
4444
keyLen: number,
45-
hash: "SHA1" | "SHA224" | "SHA256" | "SHA384" | "SHA512"
45+
algorithm: "SHA1" | "SHA224" | "SHA256" | "SHA384" | "SHA512"
4646
): Promise<ArrayBuffer>;
4747
}
4848

index.js

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -111,16 +111,27 @@ const HMAC = {
111111
};
112112

113113
const PBKDF2 = {
114-
hash: async function (password, saltArrayBuffer, iterations, keyLength, hash) {
115-
const saltBase64 = convertArrayBufferToBase64(saltArrayBuffer);
114+
hash: async function (password, salt, iterations, keyLength, algorithm) {
115+
let passwordToHash = password;
116+
let saltToHash = salt;
117+
118+
if (typeof password === 'string') {
119+
passwordToHash = convertUtf8ToArrayBuffer(password);
120+
}
121+
122+
if (typeof salt === 'string') {
123+
saltToHash = convertUtf8ToArrayBuffer(salt);
124+
}
125+
116126
const hashHex = await NativeModules.Pbkdf2.hash(
117-
password,
118-
saltBase64,
127+
convertArrayBufferToBase64(passwordToHash),
128+
convertArrayBufferToBase64(saltToHash),
119129
iterations,
120130
keyLength,
121-
hash
131+
algorithm
122132
);
123-
return (convertHexToArrayBuffer(hashHex));
133+
134+
return convertBase64ToArrayBuffer(hashHex);
124135
}
125136
};
126137

ios/RCTCrypto.xcodeproj/project.pbxproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@
254254
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
255255
GCC_WARN_UNUSED_FUNCTION = YES;
256256
GCC_WARN_UNUSED_VARIABLE = YES;
257-
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
257+
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
258258
MTL_ENABLE_DEBUG_INFO = YES;
259259
ONLY_ACTIVE_ARCH = YES;
260260
SDKROOT = iphoneos;
@@ -300,7 +300,7 @@
300300
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
301301
GCC_WARN_UNUSED_FUNCTION = YES;
302302
GCC_WARN_UNUSED_VARIABLE = YES;
303-
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
303+
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
304304
MTL_ENABLE_DEBUG_INFO = NO;
305305
SDKROOT = iphoneos;
306306
VALIDATE_PRODUCT = YES;

ios/RCTCrypto/RCTPbkdf2.m

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,15 @@ @implementation RCTPbkdf2
55

66
RCT_EXPORT_MODULE()
77

8-
RCT_EXPORT_METHOD(hash:(NSString *)password saltBase64:(NSString *)saltBase64 iterations:(int)iterations keyLen:(int)keyLen hash:(NSString *)hash
8+
RCT_EXPORT_METHOD(hash:(NSString *)password
9+
salt:(NSString *)salt
10+
iterations:(int)iterations
11+
keyLen:(int)keyLen
12+
algorithm:(NSString *)algorithm
913
resolver:(RCTPromiseResolveBlock)resolve
1014
rejecter:(RCTPromiseRejectBlock)reject) {
1115
NSError *error = nil;
12-
NSString *data = [Pbkdf2 hash:password saltBase64:saltBase64 iterations:iterations keyLen:keyLen hash:hash];
16+
NSString *data = [Pbkdf2 hash:password salt:salt iterations:iterations keyLen:keyLen algorithm:algorithm];
1317
if (data == nil) {
1418
reject(@"keygen_fail", @"Key generation failed", error);
1519
} else {

ios/RCTCrypto/lib/Pbkdf2.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#import <Foundation/Foundation.h>
22

33
@interface Pbkdf2 : NSObject
4-
+ (NSString *) hash:(NSString *)password saltBase64: (NSString *)saltBase64 iterations: (int)iterations keyLen: (int)keyLen hash: (NSString *)hash;
4+
+ (NSString *) hash:(NSString *)password salt: (NSString *)salt iterations: (int)iterations keyLen: (int)keyLen algorithm: (NSString *)algorithm;
55
@end

ios/RCTCrypto/lib/Pbkdf2.m

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77

88
@implementation Pbkdf2
99

10-
+ (NSString *) hash:(NSString *)password saltBase64: (NSString *)saltBase64 iterations: (int)iterations keyLen: (int)keyLen hash: (NSString *)hash {
10+
+ (NSString *) hash:(NSString *)password salt: (NSString *)salt iterations: (int)iterations keyLen: (int)keyLen algorithm: (NSString *)algorithm {
1111
// Data of String to generate Hash key(hexa decimal string).
12-
NSData *passwordData = [password dataUsingEncoding:NSUTF8StringEncoding];
13-
NSData *saltData = [[NSData alloc]initWithBase64EncodedString:saltBase64 options:0];
12+
NSData *passwordData = [[NSData alloc]initWithBase64EncodedString:password options:0];
13+
NSData *saltData = [[NSData alloc]initWithBase64EncodedString:salt options:0];
1414

1515
// Hash key (hexa decimal) string data length.
1616
NSMutableData *hashKeyData = [NSMutableData dataWithLength:keyLen];
@@ -23,7 +23,7 @@ + (NSString *) hash:(NSString *)password saltBase64: (NSString *)saltBase64 iter
2323
@"SHA512" : [NSNumber numberWithInt:kCCPRFHmacAlgSHA512],
2424
};
2525

26-
int alg = [[algMap valueForKey:hash] intValue];
26+
int alg = [[algMap valueForKey:algorithm] intValue];
2727

2828
// Key Derivation using PBKDF2 algorithm.
2929
int status = CCKeyDerivationPBKDF(
@@ -42,7 +42,7 @@ + (NSString *) hash:(NSString *)password saltBase64: (NSString *)saltBase64 iter
4242
return @"";
4343
}
4444

45-
return [Shared toHex:hashKeyData];
45+
return [hashKeyData base64EncodedStringWithOptions:0];
4646
}
4747

4848
@end

0 commit comments

Comments
 (0)