Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -43,19 +43,16 @@ private SessionController() {}
public static void login(Context ctx) throws SQLException {
LoginRequest request = jsonDecode(ctx, LoginRequest.class);
int teamNum = request.team();
String username = request.username()
.trim();

byte[] salt = userDB().getSalt(teamNum, username);
byte[] salt = userDB().getSalt(teamNum, request.username());
if (salt == null) {
throw userNotFound(teamNum, username);
throw userNotFound(teamNum, request.username());
}

byte[] nonce = new byte[16];
SECURE_RANDOM.nextBytes(nonce);
System.arraycopy(request.clientNonce(), 0, nonce, 0, 8);

String nonceId = username + "@" + teamNum + ":" + base64Encode(nonce);
String nonceId = request.username() + "@" + teamNum + ":" + base64Encode(nonce);
NONCES.add(nonceId);

ctx.json(new LoginChallenge(salt, nonce));
Expand All @@ -78,35 +75,33 @@ public static void login(Context ctx) throws SQLException {
public static void auth(Context ctx)
throws NoSuchAlgorithmException, InvalidKeyException, SQLException {
AuthRequest request = jsonDecode(ctx, AuthRequest.class);
String username = request.username()
.trim();
int teamNum = request.team();

User user = userDB().getUser(teamNum, username);
User user = userDB().getUser(teamNum, request.username());
if (user == null) {
throw userNotFound(teamNum, username);
throw userNotFound(teamNum, request.username());
}

Team team = teamDB().getTeam(teamNum);
if (team == null) {
throw teamNotFound(teamNum);
}

String nonceId = username + "@" + teamNum + ":" + base64Encode(request.nonce());
String nonceId = request.username() + "@" + teamNum + ":" + base64Encode(request.nonce());
if (!NONCES.contains(nonceId)) {
throw incorrectCredentials(teamNum, username);
throw incorrectCredentials(teamNum, request.username());
}

MessageDigest hashFunction = MessageDigest.getInstance(HASH_ALGORITHM);
Mac hmacFunction = Mac.getInstance(MAC_ALGORITHM);
hmacFunction.init(new SecretKeySpec(user.storedKey(), MAC_ALGORITHM));

byte[] userAndNonce = combine(teamNum + username, request.nonce());
byte[] userAndNonce = combine(teamNum + request.username(), request.nonce());
byte[] clientSignature = hmacFunction.doFinal(userAndNonce);
byte[] clientKey = xor(request.clientProof(), clientSignature);
byte[] storedKey = hashFunction.digest(clientKey);
if (!MessageDigest.isEqual(user.storedKey(), storedKey)) {
throw incorrectCredentials(teamNum, username);
throw incorrectCredentials(teamNum, request.username());
}

hmacFunction.init(new SecretKeySpec(user.serverKey(), MAC_ALGORITHM));
Expand Down Expand Up @@ -166,15 +161,25 @@ private static byte[] combine(String username, byte[] nonce) {

public record LoginRequest(@JsonProperty(required = true) int team,
@JsonProperty(required = true) String username,
@JsonProperty(required = true) byte[] clientNonce) {}
@JsonProperty(required = true) byte[] clientNonce) {
@Override
public String username() {
return username.trim();
}
}

public record LoginChallenge(byte[] salt,
byte[] nonce) {}

public record AuthRequest(@JsonProperty(required = true) String username,
@JsonProperty(required = true) int team,
@JsonProperty(required = true) byte[] nonce,
@JsonProperty(required = true) byte[] clientProof) {}
@JsonProperty(required = true) byte[] clientProof) {
@Override
public String username() {
return username.trim();
}
}

public record AuthResponse(User user,
Team team,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,17 +57,17 @@ public static void registerUser(Context ctx) throws SQLException {
}

UserRegistration registration = jsonDecode(ctx, UserRegistration.class);
String username = registration.username().trim();
if (userDB().getUser(teamNum, username) != null) {
throw userConflict(teamNum, username);
User duplicate = userDB().getUser(teamNum, registration.username());
if (duplicate != null) {
throw userConflict(teamNum, duplicate.username());
}

byte[][] auth = computeAuthentication(registration.password());
byte[] salt = auth[0];
byte[] storedKey = auth[1];
byte[] serverKey = auth[2];

User user = userDB().registerUser(teamNum, username, registration.fullName(),
User user = userDB().registerUser(teamNum, registration.username(), registration.fullName(),
registration.admin(), salt, storedKey, serverKey);
ctx.json(user);
ctx.status(HttpStatus.CREATED);
Expand Down Expand Up @@ -176,8 +176,12 @@ public static void editUser(Context ctx) throws SQLException {
session.verifyAdmin();
}

if (edits.username() != null && userDB().getUser(user.team(), edits.username()) != null) {
throw userConflict(user.team(), edits.username());
if (edits.username() != null && !edits.username()
.equals(user.username())) {
User duplicate = userDB().getUser(user.team(), edits.username());
if (duplicate != null) {
throw userConflict(user.team(), duplicate.username());
}
}

byte[][] authInfo = null;
Expand Down Expand Up @@ -258,10 +262,20 @@ private static byte[][] computeAuthentication(String password) {
record UserRegistration(@JsonProperty(required = true) String username,
@JsonProperty(required = true) String fullName,
@JsonProperty(required = true) boolean admin,
@JsonProperty(required = true) String password) {}
@JsonProperty(required = true) String password) {
@Override
public String username() {
return username.trim();
}
}

record UserEdits(String username,
String fullName,
Boolean admin,
String password) {}
String password) {
@Override
public String username() {
return username.trim();
}
}
}