Skip to content
This repository has been archived by the owner on Oct 19, 2024. It is now read-only.

Commit

Permalink
server: Add utils for validating X25519 shared key
Browse files Browse the repository at this point in the history
  • Loading branch information
M3DZIK committed Nov 12, 2023
1 parent ed61ca5 commit d5aeec6
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ import dev.medzik.librepass.server.services.EmailService
import dev.medzik.librepass.server.utils.Response
import dev.medzik.librepass.server.utils.ResponseHandler
import dev.medzik.librepass.server.utils.Validator
import dev.medzik.librepass.server.utils.Validator.validateSharedKey
import dev.medzik.librepass.server.utils.toResponse
import dev.medzik.librepass.types.api.*
import dev.medzik.librepass.utils.TOTP
import dev.medzik.librepass.utils.fromHexString
import dev.medzik.librepass.utils.toHexString
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
Expand Down Expand Up @@ -80,8 +80,7 @@ class AuthController
)
return ResponseError.INVALID_BODY.toResponse()

val sharedKey = X25519.computeSharedSecret(ServerPrivateKey, request.publicKey.fromHexString())
if (!request.sharedKey.fromHexString().contentEquals(sharedKey))
if (!validateSharedKey(request.publicKey, request.sharedKey))
return ResponseError.INVALID_CREDENTIALS.toResponse()

val verificationToken = Random.randBytes(16).toHexString()
Expand Down Expand Up @@ -205,8 +204,7 @@ class AuthController
if (emailVerificationRequired && !user.emailVerified)
return ResponseError.EMAIL_NOT_VERIFIED.toResponse()

val sharedKey = X25519.computeSharedSecret(ServerPrivateKey, user.publicKey.fromHexString())
if (!request.sharedKey.fromHexString().contentEquals(sharedKey))
if (!validateSharedKey(user, request.sharedKey))
return ResponseError.INVALID_CREDENTIALS.toResponse()

val apiToken =
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package dev.medzik.librepass.server.controllers.api

import dev.medzik.libcrypto.Random
import dev.medzik.libcrypto.X25519
import dev.medzik.librepass.responses.ResponseError
import dev.medzik.librepass.server.components.AuthorizedUser
import dev.medzik.librepass.server.controllers.advice.InvalidTwoFactorCodeException
Expand All @@ -10,13 +8,12 @@ import dev.medzik.librepass.server.database.UserRepository
import dev.medzik.librepass.server.database.UserTable
import dev.medzik.librepass.server.utils.Response
import dev.medzik.librepass.server.utils.ResponseHandler
import dev.medzik.librepass.server.utils.Validator.validateSharedKey
import dev.medzik.librepass.server.utils.toResponse
import dev.medzik.librepass.types.api.ChangePasswordRequest
import dev.medzik.librepass.types.api.SetupTwoFactorRequest
import dev.medzik.librepass.types.api.SetupTwoFactorResponse
import dev.medzik.librepass.utils.TOTP
import dev.medzik.librepass.utils.fromHexString
import dev.medzik.librepass.utils.toHexString
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.http.HttpStatus
import org.springframework.web.bind.annotation.*
Expand All @@ -36,13 +33,11 @@ class UserController
@RequestBody body: ChangePasswordRequest
): Response {
// validate shared key with an old public key
val oldSharedKey = X25519.computeSharedSecret(ServerPrivateKey, user.publicKey.fromHexString())
if (!body.oldSharedKey.fromHexString().contentEquals(oldSharedKey))
if (!validateSharedKey(user, body.oldSharedKey))
return ResponseError.INVALID_CREDENTIALS.toResponse()

// validate shared key with a new public key
val newSharedKey = X25519.computeSharedSecret(ServerPrivateKey, body.newPublicKey.fromHexString())
if (!body.newSharedKey.fromHexString().contentEquals(newSharedKey))
if (!validateSharedKey(body.newPublicKey, body.newSharedKey))
return ResponseError.INVALID_CREDENTIALS.toResponse()

// get all user cipher ids
Expand Down Expand Up @@ -86,15 +81,13 @@ class UserController
@AuthorizedUser user: UserTable,
@RequestBody body: SetupTwoFactorRequest
): Response {
// validate shared key with a new public key
val sharedKey = X25519.computeSharedSecret(ServerPrivateKey, user.publicKey.fromHexString())
if (!body.sharedKey.fromHexString().contentEquals(sharedKey))
if (!validateSharedKey(user, body.sharedKey))
return ResponseError.INVALID_CREDENTIALS.toResponse()

if (body.code != TOTP.getTOTPCode(body.secret))
throw InvalidTwoFactorCodeException()

val recoveryCode = Random.randBytes(32).toHexString()
val recoveryCode = UUID.randomUUID().toString()

userRepository.save(
user.copy(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package dev.medzik.librepass.server.utils

import dev.medzik.libcrypto.X25519
import dev.medzik.librepass.server.controllers.api.ServerPrivateKey
import dev.medzik.librepass.server.database.UserTable
import dev.medzik.librepass.utils.fromHexString
import org.apache.commons.validator.routines.EmailValidator
import java.util.regex.Pattern

Expand All @@ -14,4 +18,20 @@ object Validator {
hex: String,
length: Int
) = REGEX_PATTERN.matcher(hex).matches() && hex.length == length * 2

fun validateSharedKey(
user: UserTable,
sharedKey: String
) = validateSharedKey(
publicKey = user.publicKey,
sharedKey
)

fun validateSharedKey(
publicKey: String,
sharedKey: String
): Boolean {
val oldSharedKey = X25519.computeSharedSecret(ServerPrivateKey, publicKey.fromHexString())
return sharedKey.fromHexString().contentEquals(oldSharedKey)
}
}

0 comments on commit d5aeec6

Please sign in to comment.