Skip to content

Commit

Permalink
User authentication with PKCS
Browse files Browse the repository at this point in the history
  • Loading branch information
berkkirtay committed Sep 8, 2024
1 parent 18830f7 commit ce77f9c
Show file tree
Hide file tree
Showing 18 changed files with 384 additions and 230 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,8 @@ go.work

# go executable file output
main

# secret files
PRIVATE_KEY
PUBLIC_KEY
SIGN
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Right now this is only a fun side project. Please contact me for any considerati
- [x] HTTP based async messagging with DB synchronization among peers (requires improvement)
- [ ] Diffie-hellman key exchange for p2p key agreement (requires improvement)
- [x] CBC AES encryption for messages
- [ ] RSA or Elliptic-curve based digital signature for verifications
- [x] RSA or Elliptic-curve based digital signature for verifications
- [ ] GUI usage instead of CLI

## Stack
Expand All @@ -22,3 +22,17 @@ Right now this is only a fun side project. Please contact me for any considerati
## Considerations
- HTTP based room messaging can be replaced by a custom protocol such that peers can communicate over a small layer on top of TCP directly.
- For production usage, as centralized lookup server can be developed for peers to connect each other over the web.
- Old-school password based authentication requires an additional layer of encryption. To improve this point, authentication must be dont with user signatures where peers validate each others signatures and hashes. This means every user should generate their own private keys and use them for communication with other peers.
In this flow:

1. A user passes a public key and a signature to a peer.

2. The peer can validate the signature with the public key and generate a authentication token for the user. To pass this token, the peer can encrypt it with the users public key.

3. The user can decrypt the encrypted authentication key with his private key and start sending requests to the peer.

4. The user can send room and message requests and the peer encrypt the room secret keys to the user which is similar to the authentication key encryption.

5. The user can decrypt the room secret key (symmetric key) and use this key to encrypt and decrypt the messages in the room.

6. Master peer can choose the renew the room master key and it can send a synchronization requests to the all users in a room. This would be easier to handle with UDP or WebSocket protocols.
15 changes: 1 addition & 14 deletions src/api/controllers/authentication.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,7 @@ func postAuthRequest(c *gin.Context) {
if err != nil {
panic(err)
}
var foundUser = user.GetUser(userBody.Id, userBody.Name)
if foundUser.Id == "" {
c.AbortWithStatus(http.StatusNotFound)
return
}
if foundUser.Password != userBody.Password {
c.AbortWithStatus(http.StatusUnauthorized)
return
}

res := auth.Authenticate(auth.CreateAuthenticationModel(
auth.WithId(foundUser.Id),
auth.WithName(foundUser.Name),
auth.WithPassword(foundUser.Password)), c)
res := auth.Authenticate(userBody, c)
if res.Token == "" {
c.AbortWithStatusJSON(http.StatusBadRequest, res)
return
Expand Down
1 change: 0 additions & 1 deletion src/api/middlewares/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ func InitializeRouters(routerGroup *gin.RouterGroup) {
controllers.AuthRouter(routerGroup)
routerGroup.Use(ValidateAuthentication())
controllers.Roomouter(routerGroup)

}

func handleGenericPanic(c *gin.Context, err any) {
Expand Down
27 changes: 27 additions & 0 deletions src/commands/file_handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright (c) 2024 Berk Kirtay

package commands

import (
"os"
)

func dumpToFile(data string, fileName string) {
file, err := os.Create(fileName)
if err != nil {
panic(err)
}
_, err = file.Write([]byte(data))
if err != nil {
panic(err)
}
defer file.Close()
}

func readFromFile(fileName string) string {
data, err := os.ReadFile(fileName)
if err != nil {
panic(err)
}
return string(data)
}
2 changes: 1 addition & 1 deletion src/commands/room_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ func joinRoom(roomId string, roomPassword string) {
}
currentRoom = room
currentRoom.RoomMasterKey = cryptography.DecryptRSA(
currentRoom.RoomMasterKey, currentUser.Signature.PrivateKey)
currentRoom.RoomMasterKey, sessionAuth.Cryptography.PrivateKey)

fmt.Printf("Joined the room. You will talk with:\n")
roomUsers = make(map[string]user.User)
Expand Down
71 changes: 55 additions & 16 deletions src/commands/user_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package commands
import (
"encoding/json"
"fmt"
"main/infra/cryptography"
"main/infra/http"
"main/services/auth"
"main/services/user"
Expand All @@ -14,39 +15,77 @@ var sessionAuth auth.AuthenticationModel
var CurrentUser user.User

func HandleRegister(command []string) {
if len(command) != 2 {
fmt.Printf("Wrong usage.\n")
return
}

name := command[1]
userCrypto := cryptography.CreateCommonCrypto(
name)
dumpToFile(userCrypto.PrivateKey, "PRIVATE_KEY")
dumpToFile(userCrypto.PublicKey, "PUBLIC_KEY")
dumpToFile(userCrypto.Sign, "SIGN")
userCrypto.PrivateKey = ""
var user user.User = user.CreateUser(
user.WithName(name),
user.WithIsPeer(true),
user.WithCryptography(userCrypto))

body, err := json.Marshal(user)
if err != nil {
fmt.Printf("Error: %s", err)
return
}
res := http.POST(assignedPeer.Address+"/users", string(body), &user)
if res.StatusCode == http.CREATED {
fmt.Printf("Successfully registered as a new peer user.\n")
fmt.Printf("Crypto files are exported.\n")
}
}

func HandleLogin(command []string) {
if len(command) != 3 {
if len(command) > 3 || len(command) < 2 {
fmt.Printf("Wrong usage.\n")
return
}

userLogin := command[1]
password := command[2]

respBody := make([]user.User, 1)
res := http.GET(assignedPeer.Address+"/users", &respBody, "id", userLogin, "name", userLogin)

if res.StatusCode != http.OK {
fmt.Printf("User %s does not exist.\n", userLogin)
return
}

fmt.Printf("Hello %s! Your authentication process is ongoing..\n", respBody[0].Name)

auth := auth.CreateAuthenticationModel(
auth.WithId(userLogin),
auth.WithName(userLogin),
auth.WithPassword(password))
login(&auth)

if auth.Token != "" {
sessionAuth = auth
var authentication auth.AuthenticationModel = auth.CreateDefaultAuthenticationModel()
if len(command) == 2 {
publicKey := readFromFile("PUBLIC_KEY")
privateKey := readFromFile("PRIVATE_KEY")
sign := readFromFile("SIGN")
userCrypto := cryptography.CreateCryptography(
cryptography.WithSign(sign),
cryptography.WithPublicKey(publicKey))
authentication = auth.CreateAuthenticationModel(
auth.WithId(userLogin),
auth.WithName(userLogin),
auth.WithCryptography(userCrypto))
login(&authentication)
if authentication.Token != "" {
authentication.Token = cryptography.DecryptRSA(authentication.Token, privateKey)
}
} else if len(command) == 3 {
password := command[2]
authentication = auth.CreateAuthenticationModel(
auth.WithId(userLogin),
auth.WithName(userLogin),
auth.WithPassword(password))
login(&authentication)
}
if authentication.Token != "" {
sessionAuth = authentication
CurrentUser = respBody[0]
http.InitializeService(sessionAuth.Cookies, sessionAuth.Id, sessionAuth.Token)
fmt.Printf("You are authorized with the user-id: %s\n", auth.Id)
fmt.Printf("You are authorized with the user-id: %s\n", authentication.Id)
} else {
fmt.Printf("Authentication process failed.. somehow.\n")
}
Expand Down
70 changes: 70 additions & 0 deletions src/infra/cryptography/cryptography.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// Copyright (c) 2024 Berk Kirtay

package cryptography

type Cryptography struct {
Sign string `json:"sign,omitempty" bson:"sign,omitempty"`
PublicKey string `json:"publicKey,omitempty" bson:"publicKey,omitempty"`
PrivateKey string `json:"privateKey,omitempty" bson:"privateKey,omitempty"`
Nonce int64 `json:"nonce,omitempty" bson:"nonce,omitempty"`
Timestamp string `json:"timestamp,omitempty" bson:"timestamp,omitempty"`
Hash string `json:"hash,omitempty" bson:"hash,omitempty"`
}

type CryptographyOption func(Cryptography) Cryptography

func WithSign(sign string) CryptographyOption {
return func(Cryptography Cryptography) Cryptography {
Cryptography.Sign = sign
return Cryptography
}
}

func WithPublicKey(publicKey string) CryptographyOption {
return func(Cryptography Cryptography) Cryptography {
Cryptography.PublicKey = publicKey
return Cryptography
}
}

func WithPrivateKey(privateKey string) CryptographyOption {
return func(Cryptography Cryptography) Cryptography {
Cryptography.PrivateKey = privateKey
return Cryptography
}
}

func WithNonce(nonce int64) CryptographyOption {
return func(Cryptography Cryptography) Cryptography {
Cryptography.Nonce = nonce
return Cryptography
}
}

func WithTimestamp(timestamp string) CryptographyOption {
return func(Cryptography Cryptography) Cryptography {
Cryptography.Timestamp = timestamp
return Cryptography
}
}

func WithHash(hash string) CryptographyOption {
return func(Cryptography Cryptography) Cryptography {
Cryptography.Hash = hash
return Cryptography
}
}

func CreateDefaultCryptography() Cryptography {
return Cryptography{}
}

func CreateCryptography(options ...CryptographyOption) *Cryptography {
Cryptography := CreateDefaultCryptography()

for _, option := range options {
Cryptography = option(Cryptography)
}

return &Cryptography
}
Loading

0 comments on commit ce77f9c

Please sign in to comment.