Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Separation of voting session time and bearer token lifetime (#207) #210

Merged
merged 1 commit into from
Aug 8, 2024
Merged
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
3 changes: 2 additions & 1 deletion admin/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -959,7 +959,8 @@ def get_hmac(cfg, userId, objType, objId, perm):

secret = settings["shared_secret"]
now = 1000*int(time.time())
message = "%s:%s:%d:%s:%d" % (userId, objType, objId, perm, now)
expiry = now+300
message = "%s:%s:%d:%s:%d:timeout-token:%d" % (userId, objType, objId, perm, expiry, now)
_hmac = hmac.new(str.encode(secret), str.encode(message), hashlib.sha256).hexdigest()
ret = 'khmac:///sha-256;%s/%s' % (_hmac, message)

Expand Down
3 changes: 2 additions & 1 deletion app/commands/Console.scala
Original file line number Diff line number Diff line change
Expand Up @@ -658,7 +658,8 @@ class ConsoleImpl extends ConsoleInterface {
case Some(time) => time
case None => System.currentTimeMillis / 1000
}
val message = s"$userId:$objType:$objId:$perm:$now"
val expiry: Long = now + 300
val message = s"$userId:$objType:$objId:$perm:$expiry:timeout-token:$now"
val hmac = Crypto.hmac(shared_secret, message)
val khmac = s"khmac:///sha-256;$hmac/$message"
khmac
Expand Down
3 changes: 2 additions & 1 deletion app/controllers/BallotboxApi.scala
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,8 @@ object BallotboxApi extends Controller with Response {
val validated = vote.validate(pks, true, electionId, voterId)
val result = DAL.votes.insertWithSession(validated)
val now: Long = System.currentTimeMillis / 1000
val message = s"$voterId:AuthEvent:$electionId:RegisterSuccessfulLogin:$now"
val expiry: Long = now + 300
val message = s"$voterId:AuthEvent:$electionId:RegisterSuccessfulLogin:$expiry:timeout-token:$now"
voteCallbackUrl.map {
url => postVoteCallback(
url
Expand Down
3 changes: 2 additions & 1 deletion app/controllers/ElectionsApi.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1540,7 +1540,8 @@ object ElectionsApi
println(s"posting to $url")
val userId: String = "admin"
val now: Long = System.currentTimeMillis / 1000
val timedAuth = s"$userId:AuthEvent:$electionId:Callback:$now"
val expiry: Long = now + 300
val timedAuth = s"$userId:AuthEvent:$electionId:Callback:$expiry:timeout-token:$now"
val hmac = Crypto.hmac(boothSecret, timedAuth)
val khmac = s"khmac:///sha-256;$hmac/$timedAuth"
val f = WS.url(url)
Expand Down
19 changes: 12 additions & 7 deletions app/utils/Actions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,17 @@ case class HMACActionHelper(
val message = authorizationHeader.substring(slashPos + 1)

val split = message.split(':')
if (split.length < 5) {
if (split.length < 7) {
Logger.warn(s"Malformed authorization header")
return Left(AuthErrorCodes.MALFORMED_USER_CREDENTIALS)
}

val rcvUserId = split.slice(0, split.length - 4).mkString(":")
val rcvObjType = split(split.length - 4)
val rcvObjId = split(split.length - 3).toLong
val rcvPerm = split(split.length - 2)
val rcvUserId = split.slice(0, split.length - 6).mkString(":")
val rcvObjType = split(split.length - 6)
val rcvObjId = split(split.length - 5).toLong
val rcvPerm = split(split.length - 4)
val rcvExpiryTime = split(split.length - 3).toLong
val rcvToken = split(split.length - 2)
val rcvTime = split(split.length - 1).toLong
val now = new java.util.Date().getTime / 1000
val diff = now - rcvTime
Expand All @@ -76,16 +78,19 @@ case class HMACActionHelper(
// if the userId is the empty string we don't mind the user
val userOk = (rcvUserId == userId || userId == "")

// Check if the current time is within the expiry timestamp
val withinExpiry = now <= rcvExpiryTime

// note that we can compare without doing contant time comparison received
// strings because that's not critical for security, only hmac is
if(compareOk && (diff < expiry) && userOk && (rcvObjType == objType) &&
if(compareOk && withinExpiry && userOk && (rcvObjType == objType) &&
(rcvObjId == objId) && permsOk)
{
return Right(true)
}

Logger.warn(
s"Failed to authorize hmac:\n\tauthorizationHeader=$authorizationHeader\tcompareOk=$compareOk\n\tdiff=$diff\n\texpiry=$expiry\n\tuserOk=$userOk\n\trcvObjType=$rcvObjType\n\tobjType=$objType\n\trcvObjId=$rcvObjId\n\tobjId=$objId\n\trcvPerm=$rcvPerm\n\tperm=$perm"
s"Failed to authorize hmac:\n\tauthorizationHeader=$authorizationHeader\tcompareOk=$compareOk\n\tdiff=$diff\n\texpiry=$expiry\n\tuserOk=$userOk\n\trcvObjType=$rcvObjType\n\tobjType=$objType\n\trcvObjId=$rcvObjId\n\tobjId=$objId\n\trcvPerm=$rcvPerm\n\tperm=$perm\n\tnow=$now\n\trcvExpiryTime=$rcvExpiryTime\n\t"
)
return Left(AuthErrorCodes.INVALID_USER_CREDENTIALS)
}
Expand Down
2 changes: 1 addition & 1 deletion test/ConsoleSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ class ConsoleSpec extends Specification with TestContexts
val console = new ConsoleImpl()
console.shared_secret = "<PASSWORD>"
val khmac = console.get_khmac("user_id", "obj_type", 11, "perms", Some(22))
khmac must be equalTo(s"khmac:///sha-256;1ba07fd2e1becf9adfe74b4f6e0814fb4c9af3e0a920e718cec287857b480856/user_id:obj_type:11:perms:22")
khmac must be equalTo(s"khmac:///sha-256;1ba07fd2e1becf9adfe74b4f6e0814fb4c9af3e0a920e718cec287857b480856/user_id:obj_type:11:perms:322:timeout-token:22")
}
} // get_khmac

Expand Down
3 changes: 2 additions & 1 deletion test/TestUtils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,8 @@ trait TestContexts {
def getAuth(userId: String, objType: String, objId: Long, perm: String) = {
val authSecret = Play.current.configuration.getString("elections.auth.secret").get
val time = (new java.util.Date().getTime / 1000)
val head = s"$userId:$objType:$objId:$perm:$time"
val expiry = time + 300
val head = s"$userId:$objType:$objId:$perm:$expiry:timeout-token:$time"

"khmac:///sha-256;" + Crypto.hmac(authSecret, head) + "/" + head
}
Expand Down
Loading