From 204bcc34b07bf56d28850a0aec15754b02a3497f Mon Sep 17 00:00:00 2001 From: 10808249 Date: Mon, 24 Oct 2022 13:23:38 -0600 Subject: [PATCH 1/7] created new endpoints --- api/authdata.go | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ main.go | 4 ++++ 2 files changed, 52 insertions(+) create mode 100644 api/authdata.go diff --git a/api/authdata.go b/api/authdata.go new file mode 100644 index 0000000..4b118bb --- /dev/null +++ b/api/authdata.go @@ -0,0 +1,48 @@ +package api + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + + "github.com/gorilla/mux" +) + +type authenticationDataRequest struct { + authenticatorCertificate string `json:"authenticatorCertificate"` +} + +type entry struct { + accountName string + accountID string + sessionPublicKey string + sessionPrivateKey string +} + +func AuthDataRetrieval(w http.ResponseWriter, r *http.Request) { + fmt.Printf("Started AuthenticationData request %s\n", r.RequestURI) + defer fmt.Printf("Finished AuthenticationData request %s\n", r.RequestURI) + + vars := mux.Vars(r) + username, ok := vars["username"] + if !ok { + jsonResponse(w, fmt.Errorf("must supply a valid username i.e. foo@bar.com"), http.StatusBadRequest) + return + } + + reqBody, _ := ioutil.ReadAll(r.Body) + var request authenticationDataRequest + json.Unmarshal(reqBody, &request) + + authenticatorCertificate := request.authenticatorCertificate + +} + +func AuthDataStorageCache(w http.ResponseWriter, r *http.Request) { + +} + +func AuthDataStorageIdempotent(w http.ResponseWriter, r *http.Request) { + +} diff --git a/main.go b/main.go index 83f8675..7d71a89 100644 --- a/main.go +++ b/main.go @@ -59,6 +59,10 @@ func main() { router.HandleFunc("/la3/account/create-finish/{username}", api.CreateFinish).Methods("POST") router.HandleFunc("/la3/account/sign-csr/{username}", api.SignCSR).Methods("POST") + router.HandleFunc("/la3/users/{username}/data", api.AuthDataRetrieval).Methods("GET") + router.HandleFunc("/la3/users/{username}/data", api.AuthDataStorageCache).Methods("POST") + router.HandleFunc("/la3/users/{username}/data", api.AuthDataStorageIdempotent).Methods("PUT") + url := fmt.Sprintf("%s:%d", cfg.Host, cfg.Port) fmt.Println("Serving Let's Authenticate version 3 API on", url) From 53fc9ff3e808d5b46f175a1fca7e6452093c6b7e Mon Sep 17 00:00:00 2001 From: 10808249 Date: Mon, 24 Oct 2022 16:22:01 -0600 Subject: [PATCH 2/7] make structures --- api/authdata.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/api/authdata.go b/api/authdata.go index 4b118bb..6f82c56 100644 --- a/api/authdata.go +++ b/api/authdata.go @@ -20,6 +20,19 @@ type entry struct { sessionPrivateKey string } +type session struct { + sessionCertificate string + geoLocation string +} + +type sessionList struct { + authenticatorName string + sessions []session +} + +type AuthenticationDataResponse struct { +} + func AuthDataRetrieval(w http.ResponseWriter, r *http.Request) { fmt.Printf("Started AuthenticationData request %s\n", r.RequestURI) defer fmt.Printf("Finished AuthenticationData request %s\n", r.RequestURI) @@ -37,6 +50,13 @@ func AuthDataRetrieval(w http.ResponseWriter, r *http.Request) { authenticatorCertificate := request.authenticatorCertificate + fmt.Print(username) + fmt.Print(authenticatorCertificate) + + // E. Relying Party Authentication + // TODO: authenticatorName from authenticatorCertificate (authenticatorList = [authenticatorName]) + // TODO: create map(domain) = [entry] + } func AuthDataStorageCache(w http.ResponseWriter, r *http.Request) { From d5ae4e3cf7e9981f41640d216744c9a81136a78d Mon Sep 17 00:00:00 2001 From: 10808249 Date: Tue, 25 Oct 2022 14:08:53 -0600 Subject: [PATCH 3/7] create decodeAuthCert --- certs/csr.go | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/certs/csr.go b/certs/csr.go index 68525aa..0aa3142 100644 --- a/certs/csr.go +++ b/certs/csr.go @@ -5,6 +5,7 @@ import ( "crypto/rand" "crypto/x509" "crypto/x509/pkix" + "encoding/pem" "math/big" "time" @@ -26,7 +27,6 @@ const nanoToSeconds int = 1000000000 const secondsToMinutes int = 60 const secondsToDays int = 86400 - // Sign an Authentication Certificate. May want to do validation of the CSR here. func SignAuthCertificate(csr *x509.CertificateRequest) (*x509.Certificate, error) { return signCSR(csr, AuthCertValidDays) @@ -95,3 +95,30 @@ func signCSR(csr *x509.CertificateRequest, activeDays int) (*x509.Certificate, e return signedCert, nil } + +func DecodeAuthCert(authenticatorCertificate string) ([]byte, error) { + return retrieveAuthCert(authenticatorCertificate, AuthCertValidDays) +} + +// RetrieveAuthCert gets the authenticator certificate, the ca's private key, and the +// number of days that the certificate should be active for and then signs the +// Certificate Signing Request using the root certificate. The function then +// returns a pointer to the resulting x509.Certificate object. +func retrieveAuthCert(authCert string, activeDays int) ([]byte, error) { + + // get the configuration + cfg := util.GetConfig() + + // rootNotBefore := time.Now() + // rootNotAfter := rootNotBefore.Add(time.Duration(nanoToSeconds * secondsToDays * RootCertValidDays)) + + AuthCertBytes := []byte(authCert) + AuthCertPemBlock, _ := pem.Decode(AuthCertBytes) + + decryptedBytes, err := x509.DecryptPEMBlock(AuthCertPemBlock, util.PackPrivateKeyToPemBytes(cfg.PrivateKey)) + if err != nil { + return nil, err + } + + return decryptedBytes, nil +} From d4ef92c361ee3046df295e9f406b343aac4fcb7d Mon Sep 17 00:00:00 2001 From: 10808249 Date: Mon, 5 Dec 2022 14:43:00 -0700 Subject: [PATCH 4/7] added map structure --- api/authdata.go | 40 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/api/authdata.go b/api/authdata.go index 6f82c56..d6ad915 100644 --- a/api/authdata.go +++ b/api/authdata.go @@ -1,11 +1,14 @@ package api import ( + "crypto/rsa" "encoding/json" "fmt" "io/ioutil" "net/http" + "github.com/Usable-Security-and-Privacy-Lab/lets-auth-ca/models" + "github.com/Usable-Security-and-Privacy-Lab/lets-auth-ca/util" "github.com/gorilla/mux" ) @@ -15,9 +18,9 @@ type authenticationDataRequest struct { type entry struct { accountName string - accountID string - sessionPublicKey string - sessionPrivateKey string + accountID uint + sessionPublicKey *rsa.PublicKey + sessionPrivateKey *rsa.PrivateKey } type session struct { @@ -55,10 +58,39 @@ func AuthDataRetrieval(w http.ResponseWriter, r *http.Request) { // E. Relying Party Authentication // TODO: authenticatorName from authenticatorCertificate (authenticatorList = [authenticatorName]) - // TODO: create map(domain) = [entry] + + /* + create map(domain) = {"domain": {accountName, accountID, sessionPublicKey, sessionPrivateKey}} + sessionList = [authenticatorName, [sessionCertificate, geoLocation]] + + {E(PBKDF(masterPassword), symmetricKey) | + E(symmetricKey, [authenticatorName] | + {"domain": {accountName, accountID, sessionPublicKey, sessionPrivateKey})} + */ + + cfg := util.GetConfig() + + var domainMap = make(map[string]entry) + + var userObj, err = models.GetUserByUsername(username) + + if err != nil { + // user isn't in database + fmt.Printf("User is not in database") + w.WriteHeader(http.StatusUnauthorized) + return + } + + domainMap[cfg.RPID] = entry{ + accountName: username, + accountID: userObj.ID, + sessionPublicKey: cfg.PublicKey, + sessionPrivateKey: cfg.PrivateKey, + } } +// What will be the difference between POST and PUT in here? func AuthDataStorageCache(w http.ResponseWriter, r *http.Request) { } From f40513d4bd44aef660b5eb7b6ac70caf9c775d19 Mon Sep 17 00:00:00 2001 From: 10808249 Date: Tue, 6 Dec 2022 13:27:26 -0700 Subject: [PATCH 5/7] implement base of data lock --- models/datalocks.go | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 models/datalocks.go diff --git a/models/datalocks.go b/models/datalocks.go new file mode 100644 index 0000000..3180563 --- /dev/null +++ b/models/datalocks.go @@ -0,0 +1,29 @@ +package models + +import ( + "gorm.io/gorm" +) + +type DataLock struct { + gorm.Model + + UserID uint +} + +func CreateDataLock(d *DataLock) error { + err := db.Create(&d).Error + return err +} + +func CountDataLock(userID uint) (uint, error) { + var count int64 + err := db.Where("user_id = ?", userID).Count(&count).Error + if err != nil { + return 0, err + } + return uint(count), nil +} + +func DeleteDataLock(userID uint) error { + return db.Where("user_id = ?", userID).Delete(&DataLock{}).Error +} From e69be123c41a51ba0c723f235e42f2aff76d3896 Mon Sep 17 00:00:00 2001 From: 10808249 Date: Tue, 6 Dec 2022 13:57:11 -0700 Subject: [PATCH 6/7] added decryption process --- api/authdata.go | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/api/authdata.go b/api/authdata.go index d6ad915..c69f0a7 100644 --- a/api/authdata.go +++ b/api/authdata.go @@ -7,6 +7,7 @@ import ( "io/ioutil" "net/http" + "github.com/Usable-Security-and-Privacy-Lab/lets-auth-ca/certs" "github.com/Usable-Security-and-Privacy-Lab/lets-auth-ca/models" "github.com/Usable-Security-and-Privacy-Lab/lets-auth-ca/util" "github.com/gorilla/mux" @@ -53,8 +54,16 @@ func AuthDataRetrieval(w http.ResponseWriter, r *http.Request) { authenticatorCertificate := request.authenticatorCertificate + decryptedBytes, err := certs.DecodeAuthCert(authenticatorCertificate) + + if err != nil { + fmt.Printf("Error in decoding authCert") + w.WriteHeader(http.StatusUnauthorized) + return + } + fmt.Print(username) - fmt.Print(authenticatorCertificate) + fmt.Print(decryptedBytes) // E. Relying Party Authentication // TODO: authenticatorName from authenticatorCertificate (authenticatorList = [authenticatorName]) @@ -72,7 +81,7 @@ func AuthDataRetrieval(w http.ResponseWriter, r *http.Request) { var domainMap = make(map[string]entry) - var userObj, err = models.GetUserByUsername(username) + userObj, err := models.GetUserByUsername(username) if err != nil { // user isn't in database @@ -92,9 +101,21 @@ func AuthDataRetrieval(w http.ResponseWriter, r *http.Request) { // What will be the difference between POST and PUT in here? func AuthDataStorageCache(w http.ResponseWriter, r *http.Request) { - + lock := models.DataLock{ + UserID: 1, + } + err := models.CreateDataLock(&lock) + if err != nil { + fmt.Printf("Cannot create lock") + w.WriteHeader(http.StatusInternalServerError) + } } func AuthDataStorageIdempotent(w http.ResponseWriter, r *http.Request) { - + count, err := models.CountDataLock(1) + if err != nil { + fmt.Printf("Cannot create lock") + w.WriteHeader(http.StatusInternalServerError) + } + print(count) } From 7be0682cc247a8551b3743811fb88d64a8fc7fb4 Mon Sep 17 00:00:00 2001 From: 10808249 Date: Wed, 7 Dec 2022 13:07:49 -0700 Subject: [PATCH 7/7] fix name and structures of models and apis --- api/authdata.go | 91 ++++++++++++++++++++++++++++++++++++++++----- main.go | 4 +- models/datalocks.go | 11 +++++- models/user.go | 16 ++++---- 4 files changed, 103 insertions(+), 19 deletions(-) diff --git a/api/authdata.go b/api/authdata.go index c69f0a7..ad2d180 100644 --- a/api/authdata.go +++ b/api/authdata.go @@ -10,6 +10,7 @@ import ( "github.com/Usable-Security-and-Privacy-Lab/lets-auth-ca/certs" "github.com/Usable-Security-and-Privacy-Lab/lets-auth-ca/models" "github.com/Usable-Security-and-Privacy-Lab/lets-auth-ca/util" + "github.com/google/uuid" "github.com/gorilla/mux" ) @@ -17,6 +18,12 @@ type authenticationDataRequest struct { authenticatorCertificate string `json:"authenticatorCertificate"` } +type authenticationValutRequest struct { + authenticatorCertificate string `json:"authenticatorCertificate"` + authenticationData string `json:"authenticationData"` + lockIdentifier string `json:"lockIdentifier"` +} + type entry struct { accountName string accountID uint @@ -100,22 +107,88 @@ func AuthDataRetrieval(w http.ResponseWriter, r *http.Request) { } // What will be the difference between POST and PUT in here? -func AuthDataStorageCache(w http.ResponseWriter, r *http.Request) { - lock := models.DataLock{ - UserID: 1, +// getting a lock +func AuthDataObtainLock(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + username, ok := vars["username"] + if !ok { + jsonResponse(w, fmt.Errorf("must supply a valid username i.e. foo@bar.com"), http.StatusBadRequest) + return + } + + reqBody, _ := ioutil.ReadAll(r.Body) + var request authenticationDataRequest + json.Unmarshal(reqBody, &request) + + authenticatorCertificate := request.authenticatorCertificate + fmt.Printf(authenticatorCertificate) + + // TODO: validate authenticator certificate. If it fails, it will give 403 error + + user, err := models.GetUserByUsername(username) + if err != nil { + // user isn't in database + fmt.Printf("User is not in database") + w.WriteHeader(http.StatusUnauthorized) + return + } + + if user.IsLocked { + fmt.Printf("already obtained the lock") + w.WriteHeader(http.StatusConflict) + } + + user.IsLocked = true + err = models.UpdateUser(&user) + if err != nil { + fmt.Printf("failed to obtain lock") + w.WriteHeader(http.StatusInternalServerError) + } + + var dataLock = models.DataLock{ + UserID: user.ID, + LockIdentifier: uuid.New(), } - err := models.CreateDataLock(&lock) + + err = models.CreateDataLock(&dataLock) if err != nil { - fmt.Printf("Cannot create lock") + fmt.Printf("failed to obtain lock") w.WriteHeader(http.StatusInternalServerError) } } -func AuthDataStorageIdempotent(w http.ResponseWriter, r *http.Request) { - count, err := models.CountDataLock(1) +// store the vault +func AuthDataStoreVault(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + username, ok := vars["username"] + if !ok { + jsonResponse(w, fmt.Errorf("must supply a valid username i.e. foo@bar.com"), http.StatusBadRequest) + return + } + + reqBody, _ := ioutil.ReadAll(r.Body) + var request authenticationValutRequest + json.Unmarshal(reqBody, &request) + + authenticatorCertificate := request.authenticatorCertificate + fmt.Printf(authenticatorCertificate) + + user, err := models.GetUserByUsername(username) if err != nil { - fmt.Printf("Cannot create lock") + // user isn't in database + fmt.Printf("User is not in database") + w.WriteHeader(http.StatusUnauthorized) + return + } + + lock, err := models.GetLockByUserID(user.ID) + if err != nil { + fmt.Printf("Cannot find lock") w.WriteHeader(http.StatusInternalServerError) } - print(count) + + if lock.LockIdentifier != uuid.MustParse(request.lockIdentifier) { + fmt.Printf("invalid lock identifier") + w.WriteHeader(http.StatusConflict) + } } diff --git a/main.go b/main.go index 7d71a89..d741df8 100644 --- a/main.go +++ b/main.go @@ -60,8 +60,8 @@ func main() { router.HandleFunc("/la3/account/sign-csr/{username}", api.SignCSR).Methods("POST") router.HandleFunc("/la3/users/{username}/data", api.AuthDataRetrieval).Methods("GET") - router.HandleFunc("/la3/users/{username}/data", api.AuthDataStorageCache).Methods("POST") - router.HandleFunc("/la3/users/{username}/data", api.AuthDataStorageIdempotent).Methods("PUT") + router.HandleFunc("/la3/users/{username}/data", api.AuthDataObtainLock).Methods("POST") + router.HandleFunc("/la3/users/{username}/data", api.AuthDataStoreVault).Methods("PUT") url := fmt.Sprintf("%s:%d", cfg.Host, cfg.Port) diff --git a/models/datalocks.go b/models/datalocks.go index 3180563..d85bc8f 100644 --- a/models/datalocks.go +++ b/models/datalocks.go @@ -1,13 +1,15 @@ package models import ( + "github.com/google/uuid" "gorm.io/gorm" ) type DataLock struct { gorm.Model - UserID uint + UserID uint + LockIdentifier uuid.UUID } func CreateDataLock(d *DataLock) error { @@ -24,6 +26,13 @@ func CountDataLock(userID uint) (uint, error) { return uint(count), nil } +func GetLockByUserID(id uint) (DataLock, error) { + l := DataLock{} + err := db.Where("user_id=?", id).First(&l).Error + + return l, err +} + func DeleteDataLock(userID uint) error { return db.Where("user_id = ?", userID).Delete(&DataLock{}).Error } diff --git a/models/user.go b/models/user.go index d0f7aab..edee321 100644 --- a/models/user.go +++ b/models/user.go @@ -6,16 +6,17 @@ import ( "gorm.io/gorm" - "github.com/duo-labs/webauthn/webauthn" "github.com/duo-labs/webauthn/protocol" + "github.com/duo-labs/webauthn/webauthn" ) // User represents the user model type User struct { gorm.Model - Username string `json:"name" gorm:"not null" validate:"required,min=2,max=25,alphanumunicode"` - DisplayName string `json:"display_name" gorm:"not null"` - Credentials []Credential `json:"credentials"` + Username string `json:"name" gorm:"not null" validate:"required,min=2,max=25,alphanumunicode"` + DisplayName string `json:"display_name" gorm:"not null"` + Credentials []Credential `json:"credentials"` + IsLocked bool `json:"is_locked"` } // NewUser creates and returns a new User @@ -25,6 +26,7 @@ func NewUser(name string) User { user.Username = name user.DisplayName = name + "@letsauth.org" user.Credentials = []Credential{} + user.IsLocked = false return user } @@ -34,7 +36,7 @@ func NewUser(name string) User { func GetUser(id uint) (User, error) { u := User{} err := db.Where("id=?", id).First(&u).Error - + return u, err } @@ -90,8 +92,8 @@ func (u User) WebAuthnCredentials() []webauthn.Credential { for i, cred := range credentials { credentialID, _ := base64.URLEncoding.DecodeString(cred.CredentialID) auth := webauthn.Authenticator{ - AAGUID: cred.Auth.AAGUID, - SignCount: cred.Auth.SignCount, + AAGUID: cred.Auth.AAGUID, + SignCount: cred.Auth.SignCount, CloneWarning: cred.Auth.CloneWarning, } wcs[i] = webauthn.Credential{