Skip to content

Commit

Permalink
Merge pull request #126 from muzzammilshahid/fix-cra-auth
Browse files Browse the repository at this point in the history
Fix CRA authenticate to include salt
  • Loading branch information
om26er authored Dec 2, 2024
2 parents 31823ea + b35bc98 commit 4a32875
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 1 deletion.
28 changes: 27 additions & 1 deletion lib/src/auth/wampcra.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import "dart:math";
import "dart:typed_data";

import "package:crypto/crypto.dart";
import "package:pointycastle/pointycastle.dart";
import "package:wampproto/messages.dart";
import "package:wampproto/src/auth/auth.dart";

Expand All @@ -14,11 +15,36 @@ class WAMPCRAAuthenticator extends IClientAuthenticator {

@override
Authenticate authenticate(Challenge challenge) {
String signed = signWampCRAChallenge(challenge.extra["challenge"], utf8.encode(_secret));
Uint8List? rawSecret;
String? saltStr = challenge.extra["salt"] as String?;
if (saltStr != null && saltStr.isNotEmpty) {
int iters = challenge.extra["iterations"] as int? ?? 0;
int keylen = challenge.extra["keylen"] as int? ?? 32;

rawSecret = deriveCRAKey(saltStr, _secret, iters, keylen);
} else {
rawSecret = Uint8List.fromList(utf8.encode(_secret));
}

String signed = signWampCRAChallenge(challenge.extra["challenge"], rawSecret);
return Authenticate(signed, {});
}
}

Uint8List deriveCRAKey(String saltStr, String secret, int iterations, int keyLength) {
final salt = utf8.encode(saltStr);
final secretBytes = utf8.encode(secret);

final iter = iterations > 0 ? iterations : 1000;
final keyLen = keyLength > 0 ? keyLength : 32;

final params = Pbkdf2Parameters(Uint8List.fromList(salt), iter, keyLen);
final pbkdf2 = KeyDerivator("SHA-256/HMAC/PBKDF2")..init(params);
final derivedKey = pbkdf2.process(Uint8List.fromList(secretBytes));

return Uint8List.fromList(base64.encode(derivedKey).codeUnits);
}

String generateNonce() {
final random = Random.secure();
final nonceBytes = Uint8List.fromList(List.generate(16, (_) => random.nextInt(256)));
Expand Down
1 change: 1 addition & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ dependencies:
crypto: ^3.0.3
msgpack_dart: ^1.0.1
pinenacl: ^0.6.0
pointycastle: ^3.9.1

dev_dependencies:
lints: ^3.0.0
Expand Down
38 changes: 38 additions & 0 deletions test/interoptests/auth_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import "package:pinenacl/ed25519.dart";
import "package:test/test.dart";

import "package:wampproto/auth.dart";
import "package:wampproto/messages.dart";
import "package:wampproto/src/auth/cryptosign.dart";
import "package:wampproto/src/auth/wampcra.dart";

Expand Down Expand Up @@ -59,6 +60,19 @@ void main() {
const testSecret = "private";
var testSecretBytes = Uint8List.fromList(testSecret.codeUnits);

const String authID = "foo";
const String salt = "salt";
const int keyLength = 32;
const int iterations = 1000;
const String craChallenge =
'''{"nonce":"cdcb3b12d56e12825be99f38f55ba43f","authprovider":"provider","authid":"foo","authrole":"anonymous","authmethod":"wampcra","session":1,"timestamp":"2024-05-07T09:25:13.307Z"}''';
final Map<String, dynamic> authExtra = {
"challenge": craChallenge,
"salt": salt,
"iterations": iterations,
"keylen": keyLength,
};

test("GenerateCRAChallenge", () async {
var challenge = generateWampCRAChallenge(1, "anonymous", "anonymous", "static");

Expand Down Expand Up @@ -89,5 +103,29 @@ void main() {
var isVerified = verifyWampCRASignature(signature.trim(), challenge.trim(), testSecretBytes);
expect(isVerified, true);
});

test("SignCRAChallengeWithSalt", () async {
final challenge = Challenge(WAMPCRAAuthenticator.type, authExtra);
final authenticator = WAMPCRAAuthenticator(authID, authExtra, testSecret);

final authenticate = authenticator.authenticate(challenge);
final saltSecret = await runCommand("auth cra derive-key $salt $testSecret -i $iterations -l $keyLength");

await runCommand("auth cra verify-signature $craChallenge ${authenticate.signature} ${saltSecret.trim()}");
});

test("VerifyCRASignatureWithSalt", () async {
final challenge = await runCommand("auth cra generate-challenge 1 $authID anonymous provider");
final saltSecret = await runCommand("auth cra derive-key $salt $testSecret -i $iterations -l $keyLength");

final signature = await runCommand("auth cra sign-challenge ${challenge.trim()} ${saltSecret.trim()}");

final isVerified = verifyWampCRASignature(
signature.trim(),
challenge.trim(),
Uint8List.fromList(saltSecret.trim().codeUnits),
);
expect(isVerified, true);
});
});
}

0 comments on commit 4a32875

Please sign in to comment.