From 53923a89a58345a2213afeeb46b6f87a9aa4a00c Mon Sep 17 00:00:00 2001 From: 10808249 Date: Thu, 22 Sep 2022 11:24:22 -0600 Subject: [PATCH 01/14] created username.go --- api/username.go | 75 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 api/username.go diff --git a/api/username.go b/api/username.go new file mode 100644 index 0000000..7eefd88 --- /dev/null +++ b/api/username.go @@ -0,0 +1,75 @@ +package api + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + + "github.com/gorilla/mux" +) + +// certificateRequest represents the CSR for a single certificate +type certificateRequest struct { + CSR string `json:"CSR"` + AuthSignature string `json:"authSignature"` + AuthenticatorCertificate string `json:"authenticatorCertificate"` +} + +func ObtainNewCertificate(w http.ResponseWriter, r *http.Request) { + fmt.Printf("Beginning serving request %s\n", r.RequestURI) + defer fmt.Printf("Finished serving request %s\n", r.RequestURI) + + pathVariables := mux.Vars(r) + username := pathVariables["username"] + + reqBody, _ := ioutil.ReadAll(r.Body) + var request certificateRequest + json.Unmarshal(reqBody, &request) + + var signedCertificate string + + // Verify Authenticator Signature + authCert, CSR, signature := requestedCert.AuthenticatorCertificate, requestedCert.CSR, requestedCert.AuthSignature + + // Sign the Certificate + certByte := []byte(CSR) + signedCert, ok := certificates.SignCert(certByte, authCert, serviceCertificateValidDays, true) + + // Verify that accountID isn't taken + err := certificates.VerifyAccountID(signedCert, username) + if err != nil { + if err.Error() == "Account already exists" { + w.WriteHeader(403) + } else { + w.WriteHeader(500) + } + } + + // TODO: Verify if the accountID is owned by user (check not to put twice) + + err = certificates.VerifyRSASignatureFromCert(authCert, CSR, signature) + // TODO: AuthenticatorCertificate must be signed by CA, and for signed this account + if err != nil { + // ASK FOR CLERIFICATION DOES THE CA RETURN BAD REQUEST? + // TODO: deal with error + continue + } + + if !ok { + // AGAIN ASK FOR CLERIFICATION + continue + } + + // Add the certificate to the database + err = certificates.AddCert(signedCert, authCert) + if err != nil { + // AGAIN ASK FOR CLERIFICATION + continue + } + + response := batchResponse{SignedCertificates: signedCertificates} + final, _ := json.Marshal(response) + + fmt.Fprint(w, string(final)) +} From 85becbd1151cb0e0c0397c613b4358dba4227cd4 Mon Sep 17 00:00:00 2001 From: 10808249 Date: Thu, 22 Sep 2022 11:52:52 -0600 Subject: [PATCH 02/14] fixed related files --- api/account.go | 43 ++++++++++++++----------------------------- api/api.go | 24 +++++++++++++++++------- main.go | 1 + 3 files changed, 32 insertions(+), 36 deletions(-) diff --git a/api/account.go b/api/account.go index eea257c..ea5cda7 100644 --- a/api/account.go +++ b/api/account.go @@ -6,30 +6,18 @@ import ( "encoding/pem" "bytes" + "crypto/x509" "fmt" "io/ioutil" "net/http" - "crypto/x509" - "github.com/gorilla/mux" "github.com/duo-labs/webauthn/protocol" + "github.com/gorilla/mux" - "github.com/Usable-Security-and-Privacy-Lab/lets-auth-ca/models" "github.com/Usable-Security-and-Privacy-Lab/lets-auth-ca/certs" + "github.com/Usable-Security-and-Privacy-Lab/lets-auth-ca/models" ) -type AuthKeyRequest struct { - AuthPublicKey string `json:"authPublicKey"` -} - -type CSRRequest struct { - CSR string `json:"CSR"` -} - -type CertificateResponse struct { - Certificate string `json:"certificate"` -} - func CreateBegin(w http.ResponseWriter, r *http.Request) { fmt.Printf("Started CreateBegin request %s\n", r.RequestURI) defer fmt.Printf("Finished CreateBegin request %s\n", r.RequestURI) @@ -90,7 +78,6 @@ func CreateBegin(w http.ResponseWriter, r *http.Request) { return } - jsonResponse(w, options, http.StatusOK) } @@ -154,10 +141,10 @@ func CreateFinish(w http.ResponseWriter, r *http.Request) { auth := models.MakeAuthenticator(&credential.Authenticator) credentialID := base64.URLEncoding.EncodeToString(credential.ID) c := &models.Credential{ - Auth: auth, - PublicKey: credential.PublicKey, - CredentialID: credentialID, - UserID: user.ID, + Auth: auth, + PublicKey: credential.PublicKey, + CredentialID: credentialID, + UserID: user.ID, } err = models.CreateCredential(c) if err != nil { @@ -172,7 +159,7 @@ func CreateFinish(w http.ResponseWriter, r *http.Request) { // print it var bodyBytes []byte fmt.Println("request:") - bodyBytes, err = ioutil.ReadAll(r.Body) + bodyBytes, err = ioutil.ReadAll(r.Body) var prettyJSON bytes.Buffer if err = json.Indent(&prettyJSON, bodyBytes, "", "\t"); err != nil { fmt.Printf("JSON parse error: %v", err) @@ -198,10 +185,9 @@ func CreateFinish(w http.ResponseWriter, r *http.Request) { fmt.Println("got key", request.AuthPublicKey) - // Store the authenticator public key authKey := &models.AuthKey{ - Key: request.AuthPublicKey, + Key: request.AuthPublicKey, UserID: user.ID, } err = models.CreateAuthKey(authKey) @@ -219,14 +205,14 @@ func CreateFinish(w http.ResponseWriter, r *http.Request) { func SignCSR(w http.ResponseWriter, r *http.Request) { fmt.Printf("Started SignCSR request %s\n", r.RequestURI) defer fmt.Printf("Finished SignCSR 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 } - + user, err := models.GetUserByUsername(username) if err != nil { // user isn't in database @@ -234,7 +220,7 @@ func SignCSR(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusUnauthorized) return } - + // Get the CSR from the request var request CSRRequest err = json.NewDecoder(r.Body).Decode(&request) @@ -270,8 +256,8 @@ func SignCSR(w http.ResponseWriter, r *http.Request) { // Second, convert the public key in the CSR into PEM format publicKeyDer, _ := x509.MarshalPKIXPublicKey(csr.PublicKey) publicKeyBlock := pem.Block{ - Type: "PUBLIC KEY", - Bytes: publicKeyDer, + Type: "PUBLIC KEY", + Bytes: publicKeyDer, } publicKey := string(pem.EncodeToMemory(&publicKeyBlock)) @@ -317,4 +303,3 @@ func jsonResponse(w http.ResponseWriter, d interface{}, c int) { w.WriteHeader(c) fmt.Fprintf(w, "%s", dj) } - diff --git a/api/api.go b/api/api.go index e66a172..21d8bd5 100644 --- a/api/api.go +++ b/api/api.go @@ -3,25 +3,35 @@ package api import ( "log" - "github.com/duo-labs/webauthn/webauthn" "github.com/Usable-Security-and-Privacy-Lab/lets-auth-ca/util" + "github.com/duo-labs/webauthn/webauthn" "github.com/go-playground/validator/v10" ) - var webAuthn *webauthn.WebAuthn var sessionStore *Store var validate *validator.Validate +type AuthKeyRequest struct { + AuthPublicKey string `json:"authPublicKey"` +} + +type CSRRequest struct { + CSR string `json:"CSR"` +} + +type CertificateResponse struct { + Certificate string `json:"certificate"` +} func Init() { var err error cfg := util.GetConfig() webAuthn, err = webauthn.New(&webauthn.Config{ - RPDisplayName: cfg.RPDisplayName, // Display Name for your site - RPID: cfg.RPID, // Generally the domain name for your site - RPOrigin: cfg.RPOrigin, // this needs to be the origin for the request, with the protocol (HTTP(S)) and port number (if not 80 for HTTP or 443 for HTTPS) + RPDisplayName: cfg.RPDisplayName, // Display Name for your site + RPID: cfg.RPID, // Generally the domain name for your site + RPOrigin: cfg.RPOrigin, // this needs to be the origin for the request, with the protocol (HTTP(S)) and port number (if not 80 for HTTP or 443 for HTTPS) }) if err != nil { log.Fatal("failed to create WebAuthn from config:", err) @@ -31,6 +41,6 @@ func Init() { if err != nil { log.Fatal("failed to create session store:", err) } - + validate = validator.New() -} \ No newline at end of file +} diff --git a/main.go b/main.go index 83f8675..7b37dea 100644 --- a/main.go +++ b/main.go @@ -58,6 +58,7 @@ func main() { router.HandleFunc("/la3/account/create-begin/{username}", api.CreateBegin).Methods("GET") 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/{username}/service", api.ObtainNewCertificate) url := fmt.Sprintf("%s:%d", cfg.Host, cfg.Port) From 052564b05673edfaba8302171189577383fc22bd Mon Sep 17 00:00:00 2001 From: 10808249 Date: Thu, 22 Sep 2022 12:02:40 -0600 Subject: [PATCH 03/14] added models/certificate module from lets-auth-2 --- models/certificates/AccountIDVerifier.go | 55 ++++ models/certificates/RSASignatureVerifier.go | 36 +++ models/certificates/certHelpers.go | 159 +++++++++++ models/certificates/cert_test.go | 15 ++ models/certificates/recovery.go | 203 +++++++++++++++ models/certificates/revocation.go | 21 ++ models/certificates/x509.go | 275 ++++++++++++++++++++ 7 files changed, 764 insertions(+) create mode 100644 models/certificates/AccountIDVerifier.go create mode 100644 models/certificates/RSASignatureVerifier.go create mode 100644 models/certificates/certHelpers.go create mode 100644 models/certificates/cert_test.go create mode 100644 models/certificates/recovery.go create mode 100644 models/certificates/revocation.go create mode 100644 models/certificates/x509.go diff --git a/models/certificates/AccountIDVerifier.go b/models/certificates/AccountIDVerifier.go new file mode 100644 index 0000000..ba61da0 --- /dev/null +++ b/models/certificates/AccountIDVerifier.go @@ -0,0 +1,55 @@ +package certificates + +import ( + "errors" + "fmt" +) + +// VerifyAccountID checks to see that accountID isn't taken by +// a different user +func VerifyAccountID(signedCert, username string) error { + // VERY SUPER IMPORANT, MYSQLTABLE ACCOUNTS NEEDS TO BE CREATED WITH ACCOUNTID (VARCHAR(100) NOT NULL and USERNAME) + + db := openDatabase() + defer db.Close() + + certificate, err := FromPem(signedCert) + if err != nil { + fmt.Println(err) + return errors.New("unable to convert certificate") + } + + // TODO: ASK ABOUT WHERE THE AUTHENTICATORS ARE PUTTING IN THE ACCOUNT ID + accountID := certificate.Subject.CommonName + + var exists int + + sql := "SELECT COUNT(1) FROM userDevice WHERE id=? AND username!=?" + + err = db.QueryRow(sql, accountID, username).Scan(&exists) + + if err != nil { + fmt.Println(err) + return errors.New("database error") + } + + if exists == 1 { + return errors.New("account already exists") + } + + // pubKey, err := PubKeyFromCert(certificate) + // if err != nil { + // fmt.Println(err) + // return errors.New("Internal Server Error") + // } + // + // sql = "INSERT userDevice(deviceCert, username, revokeFlag, deviceExp, pubKey, name) VALUES (?, ?, ?, ?, ?)" + // _, err = db.Exec(sql, signedCert username, accountID, signedCert, string(pubKey), "depreicated") + + // if err != nil { + // fmt.Println(err) + // return errors.New("Database error") + // } + + return nil +} diff --git a/models/certificates/RSASignatureVerifier.go b/models/certificates/RSASignatureVerifier.go new file mode 100644 index 0000000..713d2ed --- /dev/null +++ b/models/certificates/RSASignatureVerifier.go @@ -0,0 +1,36 @@ +package certificates + +import ( + "crypto" + "crypto/rsa" + "crypto/sha256" + "encoding/base64" + // "fmt" +) + +// VerifyRSASignatureFromCert verifies an RSA Signature (with SHA-256) for some string data +func VerifyRSASignatureFromCert(certWithKey, stringData, base64Signature string) error { + // get signature from base64 into bytes + signature, err := base64.StdEncoding.DecodeString(base64Signature) + + if err != nil { + return err + } + + // get message into bytes for hashing + message := []byte(stringData) + hashed := sha256.Sum256(message) + + // get the public key from the certificate + // parse the certificate + cert, err := FromPem(certWithKey) + + if err != nil { + return err + } + + key := cert.PublicKey + + // dot the actual verification of the signature + return rsa.VerifyPKCS1v15(key.(*rsa.PublicKey), crypto.SHA256, hashed[:], signature) +} diff --git a/models/certificates/certHelpers.go b/models/certificates/certHelpers.go new file mode 100644 index 0000000..f94cadc --- /dev/null +++ b/models/certificates/certHelpers.go @@ -0,0 +1,159 @@ +package certificates + +import ( + "crypto/rsa" + "crypto/x509" + "database/sql" + "encoding/gob" + "encoding/pem" + "errors" + "fmt" + "os" + "time" + + // This import is blank because that's how the documentation tells us how to use it + "github.com/byu-ilab/Let-Auth-2/internal/dbManagers" + _ "github.com/go-sql-driver/mysql" +) + +// TagDevice is the struct used to check authorization and ... (TODO ADD MROE INFO) +type TagDevice struct { + DeviceCert string `json:"devcieCert"` + Username string `json:"username"` + RevFlag string `json:"revokeFlag"` + DeviceExp string `json:"deviceExp"` +} + +// checkError does what it says +func checkError(err error) { + if err != nil { + fmt.Println("Fatal error ", err.Error()) + os.Exit(1) + } +} + +// FromPem takes in a PEM certificate (string) and returns an x.509 certificate pointer object +func FromPem(pemCert string) (*x509.Certificate, error) { + // Decode passed in pemCert to a block of data + pemBlock, _ := pem.Decode([]byte(pemCert)) + if pemBlock == nil { + return nil, errors.New("invalid PEM string for Certificate") + } + + // Parse and return pemBlock + return x509.ParseCertificate(pemBlock.Bytes) +} + +// PubKeyFromCert gets the public key bytes for the passed in certificate pointer (cert) +func PubKeyFromCert(cert *x509.Certificate) ([]byte, error) { + + var ( + pubKey []byte = nil // used to hold public key + err error = nil // used hold possible errors + ) + + if cert == nil { + err = errors.New("passed in a bad cert pointer") + } else { + pubKey = pem.EncodeToMemory( + &pem.Block{ + Type: "RSA PUBLIC KEY", + Bytes: x509.MarshalPKCS1PublicKey(cert.PublicKey.(*rsa.PublicKey)), + }, + ) + } + + return pubKey, err +} + +// openDatabase opens a sql database connection +func openDatabase() *sql.DB { + fmt.Println("Opening up old database (model...certHelpers)") + + // Open db connection + db, err := sql.Open("mysql", dbManagers.PassDBstr) + if err != nil { + panic(err.Error()) + } + + // Double Check if db connection is open + err = db.Ping() + if err != nil { + panic(err.Error()) + } + + return db +} + +// authNotRevoked checs to see if an authenticator certificate is in the database +// and not revoked (doesn't worry abou texpieration) +// POSIBILITY TO ADD ERROR HANDELING +func authNotRevoked(authCert string) bool { + + var ( + // Used to indicate if the certificate is invalid + revFlag string + // database connection + db = openDatabase() + // the return value + notRevoked = false + ) + + // Query the database for the given authCert + err := db.QueryRow("SELECT revokeFlag FROM userDevice WHERE deviceCert = ?", authCert).Scan(&revFlag) + if revFlag == "1" { // 1 indicates that the certificate is valid + notRevoked = true + } else if err != nil { + fmt.Println("Error in retrieving row for deviceCert in authNotRevoked().") + } else { + fmt.Println("Device revoked.") + } + return notRevoked + +} + +// authorize checs the database to make sure a device certificate is valid +// todo implement this puppy! +func authorize(deviceCert string) bool { + + db := openDatabase() + defer db.Close() + + deviceCertObject, err := FromPem(deviceCert) + if err != nil { + fmt.Println("Bad device cert PEM in authorize().") + return false + } + + if deviceCertObject.NotAfter.Before(time.Now()) { // notAfter is before now, cert is expired + fmt.Println("DeviceCert expiered in authorize()") + return false + } + + var tagOne TagDevice + err = db.QueryRow("SELECT username, revokeFlag FROM userDevice WHERE deviceCert = ?", deviceCert).Scan(&tagOne.Username, &tagOne.RevFlag) + + if err != nil { + fmt.Println("Authorization failed") + fmt.Println(err) + return false + } + + // if it's not associated with a username or has been revoked + if tagOne.Username == "" || tagOne.RevFlag == "0" { + fmt.Println("Device has been revoked or invalid account") + return false + } + + return true +} + +// loads a key from a GOB encoded file +func LoadKey(fileName string, key interface{}) { + inFile, err := os.Open(fileName) + checkError(err) + decoder := gob.NewDecoder(inFile) + err = decoder.Decode(key) + checkError(err) + inFile.Close() +} diff --git a/models/certificates/cert_test.go b/models/certificates/cert_test.go new file mode 100644 index 0000000..2515904 --- /dev/null +++ b/models/certificates/cert_test.go @@ -0,0 +1,15 @@ +package certificates + +import ( + "crypto/rsa" + "testing" +) + +func TestLoadKey(t *testing.T) { + var key rsa.PrivateKey + LoadKey("../../certs/letsauthprivate.key", &key) + if key.Size() == 0 { + t.Fatalf("Error in loading private key") + } + +} diff --git a/models/certificates/recovery.go b/models/certificates/recovery.go new file mode 100644 index 0000000..f520410 --- /dev/null +++ b/models/certificates/recovery.go @@ -0,0 +1,203 @@ +package certificates + +import ( + "errors" + "fmt" + "os/exec" + "time" +) + +var ( + databaseLocks = make(map[string]databaseLock) +) + +func CheckEtag(username, etag string) bool { + db := openDatabase() + defer db.Close() + + var tag string + sql := "SELECT etag FROM etags WHERE username=?" + err := db.QueryRow(sql, username).Scan(&tag) + if err != nil { + fmt.Println(err.Error()) + return false + } + + return etag == tag +} + +type databaseLock struct { + Username string + LockString string +} + +func RetrieveRecoveryDataEtag(authCertificate string) string { + + db := openDatabase() + defer db.Close() + + certificate, err := FromPem(authCertificate) + if err != nil { + return "unable to convert authcertPEM to certificate in RetrieveRecoveryDataEtag" + } + + pubKey, err := PubKeyFromCert(certificate) + if err != nil { + return "unable to get pubkey from certificate in RetrieveRecoveryDataEtag" + } + + var recoveryData string + sql := "SELECT eblob FROM certs WHERE pubKey=?" + err = db.QueryRow(sql, pubKey).Scan(&recoveryData) + + if err != nil { + fmt.Println(err.Error()) + return "unable to read eblob from certs in RetrieveRecoveryDataEtag" + } + + return recoveryData +} + +func RetrieveRecoveryData(authCertificate, username string) (string, string, error) { + + db := openDatabase() + defer db.Close() + + fmt.Println(authCertificate) + + err := verifyAuthCert(authCertificate, username) + if err != nil { + return "", "", errors.New("invalid auth cert passed into RetrievalRecovery") + } + + var recoveryData string + sql := "SELECT rdata FROM etags WHERE username=?" + err = db.QueryRow(sql, username).Scan(&recoveryData) + + if err != nil { + fmt.Println(err.Error()) + return "", "", errors.New("unable to read eblob from certs in RetrieveRecoveryData") + } + + // var etag string + // sql = "SELECT etag FROM etags WHERE username=?" + // err = db.QueryRow(sql, username).Scan(&etag) + + // if err != nil { + // fmt.Println(err.Error()) + // return "", "", errors.New("unable to read etag from etags in RetrieveRecoveryData") + // } + + return recoveryData, "etag", nil +} + +func PutRecoveryData(username, recoveryData string) (string, error) { + + db := openDatabase() + defer db.Close() + + // next insert into the database if the certificate wasn't previously stored + sql := "INSERT INTO etags(rdata, etag, username) VALUES (?, ?, ?)" + + _, err := db.Exec(sql, recoveryData, "etag", username) + if err != nil { + return "", err + } + + newEtag := updateEtag(username) + + return newEtag, nil +} + +func UpdateRecoveryData(authCertificate, username, recoveryData, lock string) (string, error) { + + db := openDatabase() + defer db.Close() + + err := verifyAuthCert(authCertificate, username) + if err != nil { + return "", errors.New("invalid auth cert") + } + + sql := "UPDATE etags SET rdata=? WHERE username=?" + _, err = db.Exec(sql, recoveryData, username) + if err != nil { + fmt.Println(err.Error()) + return "", errors.New("failed to update certificate") + } + + newEtag := updateEtag(username) + + return newEtag, nil +} + +func updateEtag(username string) string { + db := openDatabase() + defer db.Close() + + out, err := exec.Command("uuidgen").Output() + if err != nil { + fmt.Println(err.Error()) + return "" + } + + newEtag := string(out) + + sql := "UPDATE etags SET etag=? WHERE username=?" + _, err = db.Exec(sql, newEtag, username) + if err != nil { + fmt.Println(err.Error()) + return "" + } + + return newEtag +} + +func GetDatabaseLock(authCertificate, username string) (string, error) { + + err := verifyAuthCert(authCertificate, username) + if err != nil { + return "", errors.New("unable to convert authcertPEM to certificate in RetrieveRecoveryData") + } + + previousLock := databaseLocks[username] + if previousLock.Username != "" { + return "", errors.New("lock already exists") + } + + // generate the uuid + out, err := exec.Command("uuidgen").Output() + if err != nil { + fmt.Println(err.Error()) + return "", errors.New("unable to generate UUID") + } + + out = out[:len(out)-1] + + var lock databaseLock + lock.Username = username + lock.LockString = string(out) + + go lockDatabase(username, lock) + + return string(out), nil +} + +func lockDatabase(username string, lock databaseLock) { + + databaseLocks[username] = lock + + time.Sleep(30 * time.Second) + UnlockDatabase(username) + +} + +func UnlockDatabase(username string) { + + fmt.Println("Deleted lock from database") + storedLock := databaseLocks[username] + + if storedLock.LockString != "" && storedLock.Username == username { + delete(databaseLocks, username) + } +} diff --git a/models/certificates/revocation.go b/models/certificates/revocation.go new file mode 100644 index 0000000..76df99f --- /dev/null +++ b/models/certificates/revocation.go @@ -0,0 +1,21 @@ +package certificates + +import ( + "errors" + "fmt" +) + +// RevokeAuthCertificate revokes an auth certificate in the database. +func RevokeAuthCertificate(authCert string) error { + db := openDatabase() + defer db.Close() + + sql := "UPDATE userDevice SET revokeFlag=0 where deviceCert=?" + _, err := db.Exec(sql, authCert) + if err != nil { + fmt.Println(err.Error()) + return errors.New("unable to update database") + } + + return nil +} diff --git a/models/certificates/x509.go b/models/certificates/x509.go new file mode 100644 index 0000000..e82971e --- /dev/null +++ b/models/certificates/x509.go @@ -0,0 +1,275 @@ +package certificates + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "errors" + "fmt" + "math/big" + "strconv" + "time" +) + +// CertMessage is used to pass websocket information +type CertMessage struct { + // Data used to identy websocket + Username string + + // Data shared between both + Certificate string `json:"certificate"` + Type string `json:"type"` + GoodUntil string `json:"goodUntil"` + PubKey string `json:"pubKey"` + Address string `json:"address"` + + // Site cert data + SavedData string `json:"savedData"` + + // Auth cert Data + AuthName string `json:"authName"` + RevFlag string `json:"revFlag"` +} + +// SignCert takes in a certificate and signs the certificate. +// Requires: Data (byte array), deviceCert (string), activeDays (string), checkDeviceCert (bool) +// Ensures: The passed in certificate (data) is signed +// Errors: Invalid Device Certificate, Invalid Date, Internal Server Error +func SignCert(data []byte, deviceCert, activeDays string, checkDeviceCert bool) (string, bool) { + + // don't check the device cert if this is for a device cert + if checkDeviceCert { + if valid := authorize(deviceCert); !valid { + return "Invalid Device Cert", false + } + } + + //get the cert out of the PEM string + cert, _ := pem.Decode(data) + var csr *x509.CertificateRequest + var err error + // var err error + if cert == nil { + csr, err = x509.ParseCertificateRequest(data) + // checkError(err) + } else { + csr, err = x509.ParseCertificateRequest(cert.Bytes) + // checkError(err) + } + + if err != nil { + return "Internal Server Error", false + } + + random := rand.Reader + + var key rsa.PrivateKey + LoadKey("/var/www/internal/certs/letsauthprivate.key", &key) + + now := time.Now() + + validDays, err := strconv.Atoi(activeDays) + if err != nil { + fmt.Println("Invalid date passed to SignCert: ", activeDays) + return "Internal Server Error", false + } + + // NOTE A PREVIOUS VERSION HAD SITE CERTS VALID FOR 10 DAYS AND DEVICE CERTS VALID FOR 7 DAYS + then := now.Add(time.Duration(60 * 60 * 24 * validDays * 1000 * 1000 * 1000)) + + LAnow := time.Now() + LAthen := now.Add(60 * 60 * 24 * 365 * 1000 * 1000 * 1000) // one year (365 days) for CA cert + + //template read in from the CSR + template := x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{ + CommonName: csr.Subject.CommonName, + }, + + EmailAddresses: csr.EmailAddresses, + NotBefore: now, + NotAfter: then, + + SubjectKeyId: []byte{1, 2, 3, 4}, + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, + BasicConstraintsValid: true, + IsCA: false, + } + + //template of the CA's certificate + letsauth := x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{ + CommonName: "letsauth.org", + Organization: []string{"Let's Authenticate"}, + }, + + EmailAddresses: []string{""}, + NotBefore: LAnow, + NotAfter: LAthen, + + SubjectKeyId: []byte{1, 2, 3, 4}, + KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + + BasicConstraintsValid: true, + IsCA: true, + } + + signed := letsauth + + // actually sign the certificate with the parent certificate's key + derBytes2, err := x509.CreateCertificate(random, &template, &signed, csr.PublicKey, &key) + checkError(err) + // shouldn't we just return an internal server error if we fail? + + certString := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes2}) + + return string(certString), true + +} + +// AddAuthCert adds an authenticator certificate to the database, linking it to the username +// returns whether or not it worked, and a potential error string +func AddAuthCert(username, authCert string) (bool, string) { + db := openDatabase() + defer db.Close() + + // fmt.Println(authCert) + + // pull necessary data from the authenticator certificate + certificate, err := FromPem(authCert) + if err != nil { + return false, "Internal Server Error" + } + expTime := certificate.NotAfter + pubKey, err := PubKeyFromCert(certificate) + if err != nil { + return false, "Internal Server Error" + } + + name := certificate.Subject.CommonName + + var exists int + // want to know if there's an authenticator with the same name for the same user with a different public key + fmt.Println("Name:", name) + fmt.Println("PubKey:", pubKey) + fmt.Println("Username:", username) + err = db.QueryRow("SELECT EXISTS(SELECT 1 FROM userDevice WHERE name=? AND pubKey!=? AND username=?)", name, pubKey, username).Scan(&exists) + fmt.Println("exists?", exists) + + if err != nil { // it already exists + return false, "Internal Server Error" + } else if exists == 1 { + return false, "Duplicate authenticator name: please choose another name." + } + + // this will update it if possible + fmt.Println("AuthCert:", authCert) + fmt.Println("PubKey:", pubKey) + fmt.Println("ExpTime:", expTime) + _, err = db.Exec("UPDATE userDevice SET deviceCert=?, revokeFlag=1, deviceExp=? WHERE pubKey=?", authCert, expTime, pubKey) + + if err != nil { + fmt.Println("Error in updating authenticator certificate: ", err.Error()) + return false, "Internal Server Error" + } + + // try to add the entry in case it wasn't updated (because it didn't exist) + // if there's already a cert in the database for the given public key, this won't add anything + fmt.Println("DeviceCert:", authCert) + fmt.Println("username:", username) + fmt.Println("revokeFlag:", 1) + fmt.Println("deviceExp:", expTime) + fmt.Println("pubKey:", pubKey) + fmt.Println("name:", name) + temp, err2 := db.Exec("INSERT IGNORE userDevice(deviceCert, username, revokeFlag, deviceExp, pubKey, name) VALUES (?, ?, ?, ?, ?, ?)", authCert, username, 1, expTime, pubKey, name) + fmt.Println(temp.RowsAffected()) + + if err2 != nil { + fmt.Println("Error", err2) + fmt.Println("Length of deviceCert: ", len(authCert)) + return false, "Internal Server Error" + } + + // add the device cert to the certificate table + fmt.Println("Before adding cert to certs table.##############################") + err = AddCert(authCert, authCert) + fmt.Println("After adding cert to certs table.##############################") + + if err != nil { + fmt.Println("Error", err) + fmt.Println("Length of deviceCert: ", len(authCert)) + return false, "Internal Server Error" + } + + return true, "" +} + +// AddCert adds a new service certificate to the database, along with it's association to a device certificate +func AddCert(certString, deviceCert string) error { + + db := openDatabase() + defer db.Close() + + certificate, err := FromPem(certString) + if err != nil { + return errors.New("unable to convert certificate FromPem in AddNewCert") + } + + notBefore := certificate.NotBefore + expTime := certificate.NotAfter + pubKey, err := PubKeyFromCert(certificate) + if err != nil { + return errors.New("unable to obtain public key from cert in AddNewCert") + } + + // first try to update the certificate in the database + sql := "UPDATE certs SET cert=?, certExp=?, lastUpdated=? WHERE pubKey=?" + if _, err = db.Exec(sql, certString, expTime, notBefore, pubKey); err != nil { + fmt.Println("Error updating cert in cert table") + fmt.Println(err) + return errors.New("unable to update certificate in the database") + } + + // next insert into the database if the certificate wasn't previously stored + sql = "INSERT IGNORE certs(cert, eblob, certExp, pubKey, deviceCertId, lastUpdated)\n" + sql += "SELECT ?, ?, ?, ?, userDevice.id, ? FROM userDevice WHERE deviceCert=?" + + _, err = db.Exec(sql, certString, "a blob", expTime, pubKey, notBefore, deviceCert) + + if err != nil { + fmt.Println(err) + return errors.New("error inserting into cert table") + } + + return nil +} + +func verifyAuthCert(authCert, username string) error { + fmt.Println("Verifying Auth Cert") + fmt.Println(username) + fmt.Println(authCert) + + db := openDatabase() + defer db.Close() + + var exists int + // want to know if there's an authenticator with the same name for the same user with a different public key + err := db.QueryRow("SELECT EXISTS(SELECT 1 FROM userDevice WHERE deviceCert=? AND username=?)", authCert, username).Scan(&exists) + fmt.Println("Exists:", exists) + + if err != nil { // it already exists + fmt.Println("Internal Error") + return errors.New("internal server error") + } else if exists == 1 { + fmt.Println("Cert Verified") + return nil + } else { + fmt.Println("Doesn't exist") + return errors.New("AuthCert Doesn't Exist") + } +} From 3cd472cc27dd330db66569b153d3422b2d91ac30 Mon Sep 17 00:00:00 2001 From: 10808249 Date: Thu, 22 Sep 2022 13:12:53 -0600 Subject: [PATCH 04/14] fix verifying process & dealing with errors --- api/username.go | 55 ++++++++++++++++++------ models/certificates/AccountIDVerifier.go | 51 +++++++++++++++++++++- 2 files changed, 92 insertions(+), 14 deletions(-) diff --git a/api/username.go b/api/username.go index 7eefd88..0488f8b 100644 --- a/api/username.go +++ b/api/username.go @@ -7,6 +7,13 @@ import ( "net/http" "github.com/gorilla/mux" + + "github.com/Usable-Security-and-Privacy-Lab/lets-auth-ca/models/certificates" +) + +var ( + serviceCertificateValidDays = "10" + authenticatorCertificateValidDays = "7" ) // certificateRequest represents the CSR for a single certificate @@ -16,6 +23,12 @@ type certificateRequest struct { AuthenticatorCertificate string `json:"authenticatorCertificate"` } +// serviceResponse represents the response made when the CA succesfully +// signs all the certificates +type serviceResponse struct { + SignedCertificate string `json:"serviceCertificate"` +} + func ObtainNewCertificate(w http.ResponseWriter, r *http.Request) { fmt.Printf("Beginning serving request %s\n", r.RequestURI) defer fmt.Printf("Finished serving request %s\n", r.RequestURI) @@ -27,48 +40,66 @@ func ObtainNewCertificate(w http.ResponseWriter, r *http.Request) { var request certificateRequest json.Unmarshal(reqBody, &request) - var signedCertificate string - // Verify Authenticator Signature - authCert, CSR, signature := requestedCert.AuthenticatorCertificate, requestedCert.CSR, requestedCert.AuthSignature + authCert, CSR, signature := request.AuthenticatorCertificate, request.CSR, request.AuthSignature // Sign the Certificate certByte := []byte(CSR) signedCert, ok := certificates.SignCert(certByte, authCert, serviceCertificateValidDays, true) // Verify that accountID isn't taken - err := certificates.VerifyAccountID(signedCert, username) + err := certificates.VerifyAccountIDTaken(signedCert, username) if err != nil { - if err.Error() == "Account already exists" { + if err.Error() == "account already taken by other user" { w.WriteHeader(403) } else { w.WriteHeader(500) } } - // TODO: Verify if the accountID is owned by user (check not to put twice) + // TODO: Verify if the accountID of user already exists (check not to put twice) + // maybe it can be combined with above statements + err = certificates.VerifyAccountIDExists(signedCert, username) + if err != nil { + if err.Error() == "account already exists by current user" { + w.WriteHeader(403) + } else { + w.WriteHeader(500) + } + } err = certificates.VerifyRSASignatureFromCert(authCert, CSR, signature) // TODO: AuthenticatorCertificate must be signed by CA, and for signed this account if err != nil { - // ASK FOR CLERIFICATION DOES THE CA RETURN BAD REQUEST? - // TODO: deal with error - continue + jsonResponse(w, err.Error(), http.StatusBadRequest) + return } if !ok { // AGAIN ASK FOR CLERIFICATION - continue + switch { + case signedCert == "Invalid Device Cert": + w.WriteHeader(403) + fmt.Fprint(w, signedCert) + fmt.Println("403, Invalid Device Cert") + // Do these need returns? ask if the old Login endpoint was exhaustively tested + default: + w.WriteHeader(500) + fmt.Fprint(w, signedCert) + fmt.Printf("500, %s\n", signedCert) + } + return } // Add the certificate to the database err = certificates.AddCert(signedCert, authCert) if err != nil { // AGAIN ASK FOR CLERIFICATION - continue + jsonResponse(w, fmt.Errorf("failed adding cert to certs table"), http.StatusInternalServerError) + return } - response := batchResponse{SignedCertificates: signedCertificates} + response := serviceResponse{SignedCertificate: signedCert} final, _ := json.Marshal(response) fmt.Fprint(w, string(final)) diff --git a/models/certificates/AccountIDVerifier.go b/models/certificates/AccountIDVerifier.go index ba61da0..17e3302 100644 --- a/models/certificates/AccountIDVerifier.go +++ b/models/certificates/AccountIDVerifier.go @@ -7,7 +7,7 @@ import ( // VerifyAccountID checks to see that accountID isn't taken by // a different user -func VerifyAccountID(signedCert, username string) error { +func VerifyAccountIDTaken(signedCert, username string) error { // VERY SUPER IMPORANT, MYSQLTABLE ACCOUNTS NEEDS TO BE CREATED WITH ACCOUNTID (VARCHAR(100) NOT NULL and USERNAME) db := openDatabase() @@ -34,7 +34,54 @@ func VerifyAccountID(signedCert, username string) error { } if exists == 1 { - return errors.New("account already exists") + return errors.New("account already taken by other user") + } + + // pubKey, err := PubKeyFromCert(certificate) + // if err != nil { + // fmt.Println(err) + // return errors.New("Internal Server Error") + // } + // + // sql = "INSERT userDevice(deviceCert, username, revokeFlag, deviceExp, pubKey, name) VALUES (?, ?, ?, ?, ?)" + // _, err = db.Exec(sql, signedCert username, accountID, signedCert, string(pubKey), "depreicated") + + // if err != nil { + // fmt.Println(err) + // return errors.New("Database error") + // } + + return nil +} + +func VerifyAccountIDExists(signedCert, username string) error { + // VERY SUPER IMPORANT, MYSQLTABLE ACCOUNTS NEEDS TO BE CREATED WITH ACCOUNTID (VARCHAR(100) NOT NULL and USERNAME) + + db := openDatabase() + defer db.Close() + + certificate, err := FromPem(signedCert) + if err != nil { + fmt.Println(err) + return errors.New("unable to convert certificate") + } + + // TODO: ASK ABOUT WHERE THE AUTHENTICATORS ARE PUTTING IN THE ACCOUNT ID + accountID := certificate.Subject.CommonName + + var exists int + + sql := "SELECT COUNT(1) FROM userDevice WHERE id=? AND username=?" + + err = db.QueryRow(sql, accountID, username).Scan(&exists) + + if err != nil { + fmt.Println(err) + return errors.New("database error") + } + + if exists == 1 { + return errors.New("account already exists by current user") } // pubKey, err := PubKeyFromCert(certificate) From dbb2bb481b9962b879c972e36e23aecf28d19f36 Mon Sep 17 00:00:00 2001 From: 10808249 Date: Fri, 23 Sep 2022 12:01:22 -0600 Subject: [PATCH 05/14] added dbManager form let-auth-2 (to be fixed) --- database/fidoDB.go | 46 ++++++++++++++++++++++++++++++ database/passDB.go | 46 ++++++++++++++++++++++++++++++ models/certificates/certHelpers.go | 5 ++-- 3 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 database/fidoDB.go create mode 100644 database/passDB.go diff --git a/database/fidoDB.go b/database/fidoDB.go new file mode 100644 index 0000000..96b8105 --- /dev/null +++ b/database/fidoDB.go @@ -0,0 +1,46 @@ +package database + +import ( + "database/sql" + "fmt" + "os" + + // This import is blank because that's how the documentation tells us how to use it + _ "github.com/go-sql-driver/mysql" +) + +// For information on how the secret is loaded into the docker container at runtime, +// see the DockerUpdate.md file in root. + +// fidoDBpass format: fido_user:[password]@tcp(localhost:3306)/fido + +var FidoDBstr string + +// openDatabase opens a sql database connection +func InitFidoDatabase() bool { + dat, err := os.ReadFile("/run/secrets/fidoDBpass") + if err != nil { + fmt.Println(err.Error()) + return false + } + FidoDBstr = string(dat) + + fmt.Println("Opening up fido database (fido test)") + + // Open db connection + db, err := sql.Open("mysql", FidoDBstr) + if err != nil { + fmt.Println(err.Error()) + return false + } + defer db.Close() + + // Double Check if db connection is open + err = db.Ping() + if err != nil { + fmt.Println(err.Error()) + return false + } + + return true +} diff --git a/database/passDB.go b/database/passDB.go new file mode 100644 index 0000000..2d3f5b2 --- /dev/null +++ b/database/passDB.go @@ -0,0 +1,46 @@ +package database + +import ( + "database/sql" + "fmt" + "os" + + // This import is blank because that's how the documentation tells us how to use it + _ "github.com/go-sql-driver/mysql" +) + +// For information on how the secret is loaded into the docker container at runtime, +// see the DockerUpdate.md file in root. + +// passDBpass format: letsauthtest:[password]@tcp(localhost:3306)/lets_auth + +var PassDBstr string + +// openDatabase opens a sql database connection +func InitPassDatabase() bool { + dat, err := os.ReadFile("/run/secrets/passDBpass") + if err != nil { + fmt.Println(err.Error()) + return false + } + PassDBstr = string(dat) + + fmt.Println("Opening up database (password test)") + + // Open db connection + db, err := sql.Open("mysql", PassDBstr) + if err != nil { + fmt.Println(err.Error()) + return false + } + defer db.Close() + + // Double Check if db connection is open + err = db.Ping() + if err != nil { + fmt.Println(err.Error()) + return false + } + + return true +} diff --git a/models/certificates/certHelpers.go b/models/certificates/certHelpers.go index f94cadc..f8b69a9 100644 --- a/models/certificates/certHelpers.go +++ b/models/certificates/certHelpers.go @@ -12,7 +12,8 @@ import ( "time" // This import is blank because that's how the documentation tells us how to use it - "github.com/byu-ilab/Let-Auth-2/internal/dbManagers" + + "github.com/Usable-Security-and-Privacy-Lab/lets-auth-ca/database" _ "github.com/go-sql-driver/mysql" ) @@ -71,7 +72,7 @@ func openDatabase() *sql.DB { fmt.Println("Opening up old database (model...certHelpers)") // Open db connection - db, err := sql.Open("mysql", dbManagers.PassDBstr) + db, err := sql.Open("mysql", database.PassDBstr) if err != nil { panic(err.Error()) } From 122e6fb7f95a36a0ae8c42b2fd5b33a12badac48 Mon Sep 17 00:00:00 2001 From: 10808249 Date: Thu, 29 Sep 2022 12:56:54 -0600 Subject: [PATCH 06/14] fixed file location reference --- api/username_test.go | 17 +++++++++++++++++ models/certificates/cert_test.go | 6 +++++- models/certificates/x509.go | 6 +++++- 3 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 api/username_test.go diff --git a/api/username_test.go b/api/username_test.go new file mode 100644 index 0000000..7ca10fe --- /dev/null +++ b/api/username_test.go @@ -0,0 +1,17 @@ +package api + +import ( + "testing" + + "github.com/stretchr/testify/mock" +) + +type mockObtainNewCertificate struct { + mock.Mock +} + +// TODO: implement tests + +func TestObtainNewCertificate(t *testing.T) { + +} diff --git a/models/certificates/cert_test.go b/models/certificates/cert_test.go index 2515904..5237084 100644 --- a/models/certificates/cert_test.go +++ b/models/certificates/cert_test.go @@ -3,11 +3,15 @@ package certificates import ( "crypto/rsa" "testing" + + "github.com/Usable-Security-and-Privacy-Lab/lets-auth-ca/util" ) func TestLoadKey(t *testing.T) { + cfg := util.GetConfig() + var key rsa.PrivateKey - LoadKey("../../certs/letsauthprivate.key", &key) + LoadKey(cfg.PrivateKeyFile, &key) if key.Size() == 0 { t.Fatalf("Error in loading private key") } diff --git a/models/certificates/x509.go b/models/certificates/x509.go index e82971e..463268d 100644 --- a/models/certificates/x509.go +++ b/models/certificates/x509.go @@ -11,6 +11,8 @@ import ( "math/big" "strconv" "time" + + "github.com/Usable-Security-and-Privacy-Lab/lets-auth-ca/util" ) // CertMessage is used to pass websocket information @@ -65,8 +67,10 @@ func SignCert(data []byte, deviceCert, activeDays string, checkDeviceCert bool) random := rand.Reader + cfg := util.GetConfig() + var key rsa.PrivateKey - LoadKey("/var/www/internal/certs/letsauthprivate.key", &key) + LoadKey(cfg.PrivateKeyFile, &key) now := time.Now() From 2487a86fbf3fbad35ab17dec523cc79291aedc17 Mon Sep 17 00:00:00 2001 From: 10808249 Date: Thu, 29 Sep 2022 14:01:21 -0600 Subject: [PATCH 07/14] fix: to use current ca code --- models/certificates/certHelpers.go | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/models/certificates/certHelpers.go b/models/certificates/certHelpers.go index f8b69a9..d878698 100644 --- a/models/certificates/certHelpers.go +++ b/models/certificates/certHelpers.go @@ -13,8 +13,10 @@ import ( // This import is blank because that's how the documentation tells us how to use it - "github.com/Usable-Security-and-Privacy-Lab/lets-auth-ca/database" + "github.com/Usable-Security-and-Privacy-Lab/lets-auth-ca/util" _ "github.com/go-sql-driver/mysql" + "gorm.io/driver/mysql" + "gorm.io/gorm" ) // TagDevice is the struct used to check authorization and ... (TODO ADD MROE INFO) @@ -71,19 +73,26 @@ func PubKeyFromCert(cert *x509.Certificate) ([]byte, error) { func openDatabase() *sql.DB { fmt.Println("Opening up old database (model...certHelpers)") - // Open db connection - db, err := sql.Open("mysql", database.PassDBstr) + cfg := util.GetConfig() + + // Open our database connection + temp_db, err := gorm.Open(mysql.Open(cfg.DbConfig), &gorm.Config{}) if err != nil { - panic(err.Error()) + return nil + } + var sqlDB *sql.DB + sqlDB, err = temp_db.DB() + if err != nil { + return nil } // Double Check if db connection is open - err = db.Ping() + err = sqlDB.Ping() if err != nil { panic(err.Error()) } - return db + return sqlDB } // authNotRevoked checs to see if an authenticator certificate is in the database From b7280a65ec850eae4ff081ac6ef663084760622d Mon Sep 17 00:00:00 2001 From: 10808249 Date: Mon, 7 Nov 2022 12:23:19 -0700 Subject: [PATCH 08/14] move legacy code --- {models => legacy}/certificates/AccountIDVerifier.go | 0 {models => legacy}/certificates/RSASignatureVerifier.go | 0 {models => legacy}/certificates/certHelpers.go | 0 {models => legacy}/certificates/cert_test.go | 0 {models => legacy}/certificates/recovery.go | 0 {models => legacy}/certificates/revocation.go | 0 {models => legacy}/certificates/x509.go | 0 {database => legacy/database}/fidoDB.go | 0 {database => legacy/database}/passDB.go | 0 9 files changed, 0 insertions(+), 0 deletions(-) rename {models => legacy}/certificates/AccountIDVerifier.go (100%) rename {models => legacy}/certificates/RSASignatureVerifier.go (100%) rename {models => legacy}/certificates/certHelpers.go (100%) rename {models => legacy}/certificates/cert_test.go (100%) rename {models => legacy}/certificates/recovery.go (100%) rename {models => legacy}/certificates/revocation.go (100%) rename {models => legacy}/certificates/x509.go (100%) rename {database => legacy/database}/fidoDB.go (100%) rename {database => legacy/database}/passDB.go (100%) diff --git a/models/certificates/AccountIDVerifier.go b/legacy/certificates/AccountIDVerifier.go similarity index 100% rename from models/certificates/AccountIDVerifier.go rename to legacy/certificates/AccountIDVerifier.go diff --git a/models/certificates/RSASignatureVerifier.go b/legacy/certificates/RSASignatureVerifier.go similarity index 100% rename from models/certificates/RSASignatureVerifier.go rename to legacy/certificates/RSASignatureVerifier.go diff --git a/models/certificates/certHelpers.go b/legacy/certificates/certHelpers.go similarity index 100% rename from models/certificates/certHelpers.go rename to legacy/certificates/certHelpers.go diff --git a/models/certificates/cert_test.go b/legacy/certificates/cert_test.go similarity index 100% rename from models/certificates/cert_test.go rename to legacy/certificates/cert_test.go diff --git a/models/certificates/recovery.go b/legacy/certificates/recovery.go similarity index 100% rename from models/certificates/recovery.go rename to legacy/certificates/recovery.go diff --git a/models/certificates/revocation.go b/legacy/certificates/revocation.go similarity index 100% rename from models/certificates/revocation.go rename to legacy/certificates/revocation.go diff --git a/models/certificates/x509.go b/legacy/certificates/x509.go similarity index 100% rename from models/certificates/x509.go rename to legacy/certificates/x509.go diff --git a/database/fidoDB.go b/legacy/database/fidoDB.go similarity index 100% rename from database/fidoDB.go rename to legacy/database/fidoDB.go diff --git a/database/passDB.go b/legacy/database/passDB.go similarity index 100% rename from database/passDB.go rename to legacy/database/passDB.go From 49e97f43adc159c7f410c7b5dd6fa1a6d9220d41 Mon Sep 17 00:00:00 2001 From: 10808249 Date: Mon, 7 Nov 2022 13:26:07 -0700 Subject: [PATCH 09/14] fixing to use current code instead of legacy --- api/username.go | 110 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 81 insertions(+), 29 deletions(-) diff --git a/api/username.go b/api/username.go index 0488f8b..60e8c18 100644 --- a/api/username.go +++ b/api/username.go @@ -1,19 +1,17 @@ package api import ( + "crypto/x509" "encoding/json" + "encoding/pem" "fmt" "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/legacy/certificates" + "github.com/Usable-Security-and-Privacy-Lab/lets-auth-ca/models" "github.com/gorilla/mux" - - "github.com/Usable-Security-and-Privacy-Lab/lets-auth-ca/models/certificates" -) - -var ( - serviceCertificateValidDays = "10" - authenticatorCertificateValidDays = "7" ) // certificateRequest represents the CSR for a single certificate @@ -43,12 +41,82 @@ func ObtainNewCertificate(w http.ResponseWriter, r *http.Request) { // Verify Authenticator Signature authCert, CSR, signature := request.AuthenticatorCertificate, request.CSR, request.AuthSignature + 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 + } + // Sign the Certificate - certByte := []byte(CSR) - signedCert, ok := certificates.SignCert(certByte, authCert, serviceCertificateValidDays, true) + cert, _ := pem.Decode([]byte(CSR)) + var csr *x509.CertificateRequest + + // var err error + if cert == nil { + csr, err = x509.ParseCertificateRequest([]byte(CSR)) + // checkError(err) + } else { + csr, err = x509.ParseCertificateRequest(cert.Bytes) + // checkError(err) + } + if err != nil { + fmt.Println("CSR bad format", err.Error()) + jsonResponse(w, err.Error(), http.StatusBadRequest) + return + } + + // Check that the CSR is for one of the valid authenticator public keys + // First, get the authorized keys for this user + authKeys, err := models.GetAuthKeysForUser(user) + if err != nil { + jsonResponse(w, "unable to get authenticator keys for this user", http.StatusInternalServerError) + return + } + // Second, convert the public key in the CSR into PEM format + publicKeyDer, _ := x509.MarshalPKIXPublicKey(csr.PublicKey) + publicKeyBlock := pem.Block{ + Type: "PUBLIC KEY", + Bytes: publicKeyDer, + } + publicKey := string(pem.EncodeToMemory(&publicKeyBlock)) + + // Third, check if the key matches any of the valid keys for this user + present := models.AuthKeyPresent(publicKey, authKeys) + if !present { + jsonResponse(w, "authenticator key is not authorized for this account", http.StatusUnauthorized) + return + } + + // TBD we need to validate the CSR. This should include being sure it is properly signed. + // It is for the username this account owns. + // What else? + if username != csr.Subject.CommonName { + jsonResponse(w, "username doesn't match CSR subject", http.StatusBadRequest) + return + } + + signedCert, err := certs.SignAuthCertificate(csr) + pemCert := string(pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: signedCert.Raw})) + + if err != nil { + switch { + case pemCert == "Invalid Device Cert": + w.WriteHeader(403) + fmt.Fprint(w, pemCert) + fmt.Println("403, Invalid Device Cert") + // Do these need returns? ask if the old Login endpoint was exhaustively tested + default: + w.WriteHeader(500) + fmt.Fprint(w, pemCert) + fmt.Printf("500, %s\n", pemCert) + } + return + } // Verify that accountID isn't taken - err := certificates.VerifyAccountIDTaken(signedCert, username) + err = certificates.VerifyAccountIDTaken(pemCert, username) if err != nil { if err.Error() == "account already taken by other user" { w.WriteHeader(403) @@ -59,7 +127,7 @@ func ObtainNewCertificate(w http.ResponseWriter, r *http.Request) { // TODO: Verify if the accountID of user already exists (check not to put twice) // maybe it can be combined with above statements - err = certificates.VerifyAccountIDExists(signedCert, username) + err = certificates.VerifyAccountIDExists(pemCert, username) if err != nil { if err.Error() == "account already exists by current user" { w.WriteHeader(403) @@ -75,31 +143,15 @@ func ObtainNewCertificate(w http.ResponseWriter, r *http.Request) { return } - if !ok { - // AGAIN ASK FOR CLERIFICATION - switch { - case signedCert == "Invalid Device Cert": - w.WriteHeader(403) - fmt.Fprint(w, signedCert) - fmt.Println("403, Invalid Device Cert") - // Do these need returns? ask if the old Login endpoint was exhaustively tested - default: - w.WriteHeader(500) - fmt.Fprint(w, signedCert) - fmt.Printf("500, %s\n", signedCert) - } - return - } - // Add the certificate to the database - err = certificates.AddCert(signedCert, authCert) + err = certificates.AddCert(pemCert, authCert) if err != nil { // AGAIN ASK FOR CLERIFICATION jsonResponse(w, fmt.Errorf("failed adding cert to certs table"), http.StatusInternalServerError) return } - response := serviceResponse{SignedCertificate: signedCert} + response := serviceResponse{SignedCertificate: pemCert} final, _ := json.Marshal(response) fmt.Fprint(w, string(final)) From adb943d9d078c134ddf0b68764c862d40f4f69cb Mon Sep 17 00:00:00 2001 From: 10808249 Date: Wed, 9 Nov 2022 13:05:18 -0700 Subject: [PATCH 10/14] changed verification function, changed endpoint --- api/username.go | 36 +++++++++++++++++++++++------------- main.go | 2 +- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/api/username.go b/api/username.go index 60e8c18..14366e0 100644 --- a/api/username.go +++ b/api/username.go @@ -116,24 +116,34 @@ func ObtainNewCertificate(w http.ResponseWriter, r *http.Request) { } // Verify that accountID isn't taken - err = certificates.VerifyAccountIDTaken(pemCert, username) - if err != nil { - if err.Error() == "account already taken by other user" { - w.WriteHeader(403) - } else { - w.WriteHeader(500) + /* + err = certificates.VerifyAccountIDTaken(pemCert, username) + if err != nil { + if err.Error() == "account already taken by other user" { + w.WriteHeader(403) + } else { + w.WriteHeader(500) + } } - } + */ // TODO: Verify if the accountID of user already exists (check not to put twice) // maybe it can be combined with above statements - err = certificates.VerifyAccountIDExists(pemCert, username) - if err != nil { - if err.Error() == "account already exists by current user" { - w.WriteHeader(403) - } else { - w.WriteHeader(500) + /* + err = certificates.VerifyAccountIDExists(pemCert, username) + if err != nil { + if err.Error() == "account already exists by current user" { + w.WriteHeader(403) + } else { + w.WriteHeader(500) + } } + */ + _, err = models.GetUserByUsername(username) + if err == nil { + fmt.Println("Attempted to register username that already exists: ", username) + jsonResponse(w, "User already exists", http.StatusConflict) + return } err = certificates.VerifyRSASignatureFromCert(authCert, CSR, signature) diff --git a/main.go b/main.go index 7b37dea..3bd15c5 100644 --- a/main.go +++ b/main.go @@ -58,7 +58,7 @@ func main() { router.HandleFunc("/la3/account/create-begin/{username}", api.CreateBegin).Methods("GET") 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/{username}/service", api.ObtainNewCertificate) + router.HandleFunc("/la3/user/{username}/service", api.ObtainNewCertificate) url := fmt.Sprintf("%s:%d", cfg.Host, cfg.Port) From 2a21b37e5c91d3e6ab349f441db981b046381b61 Mon Sep 17 00:00:00 2001 From: 10808249 Date: Mon, 14 Nov 2022 12:54:01 -0700 Subject: [PATCH 11/14] created new map for accountid and username --- api/username.go | 4 +++- api/username_test.go | 4 ++-- main.go | 2 +- models/accountmap.go | 34 ++++++++++++++++++++++++++++++++++ models/models.go | 9 ++++----- 5 files changed, 44 insertions(+), 9 deletions(-) create mode 100644 models/accountmap.go diff --git a/api/username.go b/api/username.go index 14366e0..71beaf3 100644 --- a/api/username.go +++ b/api/username.go @@ -27,7 +27,7 @@ type serviceResponse struct { SignedCertificate string `json:"serviceCertificate"` } -func ObtainNewCertificate(w http.ResponseWriter, r *http.Request) { +func ObtainAccountCertificate(w http.ResponseWriter, r *http.Request) { fmt.Printf("Beginning serving request %s\n", r.RequestURI) defer fmt.Printf("Finished serving request %s\n", r.RequestURI) @@ -139,6 +139,8 @@ func ObtainNewCertificate(w http.ResponseWriter, r *http.Request) { } } */ + + // TODO: new identifier (only one time) _, err = models.GetUserByUsername(username) if err == nil { fmt.Println("Attempted to register username that already exists: ", username) diff --git a/api/username_test.go b/api/username_test.go index 7ca10fe..ffa9deb 100644 --- a/api/username_test.go +++ b/api/username_test.go @@ -6,12 +6,12 @@ import ( "github.com/stretchr/testify/mock" ) -type mockObtainNewCertificate struct { +type mockObtainAccountCertificate struct { mock.Mock } // TODO: implement tests -func TestObtainNewCertificate(t *testing.T) { +func TestObtainAccountCertificate(t *testing.T) { } diff --git a/main.go b/main.go index 3bd15c5..5bf4386 100644 --- a/main.go +++ b/main.go @@ -58,7 +58,7 @@ func main() { router.HandleFunc("/la3/account/create-begin/{username}", api.CreateBegin).Methods("GET") 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/user/{username}/service", api.ObtainNewCertificate) + router.HandleFunc("/la3/user/{username}/account", api.ObtainAccountCertificate) url := fmt.Sprintf("%s:%d", cfg.Host, cfg.Port) diff --git a/models/accountmap.go b/models/accountmap.go new file mode 100644 index 0000000..a3dcf85 --- /dev/null +++ b/models/accountmap.go @@ -0,0 +1,34 @@ +package models + +import ( + "crypto/x509" + + "gorm.io/gorm" +) + +type AccountMap struct { + gorm.Model + + username string + accountID uint +} + +func MakeAccoutMap(username string, accountID uint) AccountMap { + accountMap := AccountMap{ + username: username, + accountID: accountID, + } + return accountMap +} + +func CreateAccountMap(a *AccountMap) error { + err := db.Create(&a).Error + return err +} + +func VerifyAccountID(certificate *x509.Certificate, username string) error { + // acc := AccountMap{} + err := db.Where("username = ?", username).Error + + return err +} diff --git a/models/models.go b/models/models.go index 379d33f..0de02ac 100644 --- a/models/models.go +++ b/models/models.go @@ -4,12 +4,12 @@ package models // https://github.com/duo-labs/webauthn.io import ( + "database/sql" "encoding/binary" "errors" - "database/sql" - "gorm.io/gorm" "gorm.io/driver/mysql" + "gorm.io/gorm" "github.com/Usable-Security-and-Privacy-Lab/lets-auth-ca/util" ) @@ -19,7 +19,6 @@ var db *gorm.DB // ErrUsernameTaken is thrown when a user attempts to register a username that is taken. var ErrUsernameTaken = errors.New("username already taken") - // BytesToID converts a byte slice to a uint. This is needed because the // WebAuthn specification deals with byte buffers, while the primary keys in // our database are uints. @@ -33,7 +32,7 @@ func BytesToID(buf []byte) uint { // It also populates the Config object func Setup(config *util.Config) error { // assume the database is already created - + // Open our database connection temp_db, err := gorm.Open(mysql.Open(config.DbConfig), &gorm.Config{}) if err != nil { @@ -51,6 +50,7 @@ func Setup(config *util.Config) error { &User{}, &Credential{}, &AuthKey{}, + &AccountMap{}, ) if err != nil { @@ -59,4 +59,3 @@ func Setup(config *util.Config) error { return nil } - From 3ff0b89a2e0f4832188ed64da879a927a5be9297 Mon Sep 17 00:00:00 2001 From: 10808249 Date: Mon, 14 Nov 2022 13:14:17 -0700 Subject: [PATCH 12/14] changed verification of account ID --- api/username.go | 36 ++++++------------------------------ models/accountmap.go | 31 +++++++++++++++++++++++++++++-- 2 files changed, 35 insertions(+), 32 deletions(-) diff --git a/api/username.go b/api/username.go index 71beaf3..51a940c 100644 --- a/api/username.go +++ b/api/username.go @@ -115,37 +115,13 @@ func ObtainAccountCertificate(w http.ResponseWriter, r *http.Request) { return } - // Verify that accountID isn't taken - /* - err = certificates.VerifyAccountIDTaken(pemCert, username) - if err != nil { - if err.Error() == "account already taken by other user" { - w.WriteHeader(403) - } else { - w.WriteHeader(500) - } - } - */ - - // TODO: Verify if the accountID of user already exists (check not to put twice) - // maybe it can be combined with above statements - /* - err = certificates.VerifyAccountIDExists(pemCert, username) - if err != nil { - if err.Error() == "account already exists by current user" { - w.WriteHeader(403) - } else { - w.WriteHeader(500) - } + err = models.VerifyAccountID(signedCert, username) + if err != nil { + if err.Error() == "account already taken by other user" || err.Error() == "account already exists by current user" { + w.WriteHeader(403) + } else { + w.WriteHeader(500) } - */ - - // TODO: new identifier (only one time) - _, err = models.GetUserByUsername(username) - if err == nil { - fmt.Println("Attempted to register username that already exists: ", username) - jsonResponse(w, "User already exists", http.StatusConflict) - return } err = certificates.VerifyRSASignatureFromCert(authCert, CSR, signature) diff --git a/models/accountmap.go b/models/accountmap.go index a3dcf85..f514426 100644 --- a/models/accountmap.go +++ b/models/accountmap.go @@ -2,6 +2,8 @@ package models import ( "crypto/x509" + "errors" + "fmt" "gorm.io/gorm" ) @@ -28,7 +30,32 @@ func CreateAccountMap(a *AccountMap) error { func VerifyAccountID(certificate *x509.Certificate, username string) error { // acc := AccountMap{} - err := db.Where("username = ?", username).Error + accountID := certificate.Subject.CommonName - return err + var exists int + var err error + + err = db.Where("accountID=? AND username!=?", accountID, username).Scan(&exists).Error + + if err != nil { + fmt.Println(err) + return errors.New("database error") + } + + if exists == 1 { + return errors.New("account already taken by other user") + } + + err = db.Where("accountID=? AND username=?", accountID, username).Scan(&exists).Error + + if err != nil { + fmt.Println(err) + return errors.New("database error") + } + + if exists == 1 { + return errors.New("account already exists by current user") + } + + return nil } From 7311ee9865cac7d53ad33b4cb0c3cf715aa135ec Mon Sep 17 00:00:00 2001 From: 10808249 Date: Mon, 14 Nov 2022 14:45:41 -0700 Subject: [PATCH 13/14] removed legacy code dependency --- api/username.go | 10 +- certs/rsa.go | 83 ++++++ legacy/certificates/AccountIDVerifier.go | 102 ------- legacy/certificates/RSASignatureVerifier.go | 36 --- legacy/certificates/certHelpers.go | 169 ------------ legacy/certificates/cert_test.go | 19 -- legacy/certificates/recovery.go | 203 -------------- legacy/certificates/revocation.go | 21 -- legacy/certificates/x509.go | 279 -------------------- legacy/database/fidoDB.go | 46 ---- legacy/database/passDB.go | 46 ---- 11 files changed, 88 insertions(+), 926 deletions(-) create mode 100644 certs/rsa.go delete mode 100644 legacy/certificates/AccountIDVerifier.go delete mode 100644 legacy/certificates/RSASignatureVerifier.go delete mode 100644 legacy/certificates/certHelpers.go delete mode 100644 legacy/certificates/cert_test.go delete mode 100644 legacy/certificates/recovery.go delete mode 100644 legacy/certificates/revocation.go delete mode 100644 legacy/certificates/x509.go delete mode 100644 legacy/database/fidoDB.go delete mode 100644 legacy/database/passDB.go diff --git a/api/username.go b/api/username.go index 51a940c..b52b277 100644 --- a/api/username.go +++ b/api/username.go @@ -9,7 +9,6 @@ import ( "net/http" "github.com/Usable-Security-and-Privacy-Lab/lets-auth-ca/certs" - "github.com/Usable-Security-and-Privacy-Lab/lets-auth-ca/legacy/certificates" "github.com/Usable-Security-and-Privacy-Lab/lets-auth-ca/models" "github.com/gorilla/mux" ) @@ -50,12 +49,13 @@ func ObtainAccountCertificate(w http.ResponseWriter, r *http.Request) { } // Sign the Certificate - cert, _ := pem.Decode([]byte(CSR)) + byteCSR := []byte(CSR) + cert, _ := pem.Decode(byteCSR) var csr *x509.CertificateRequest // var err error if cert == nil { - csr, err = x509.ParseCertificateRequest([]byte(CSR)) + csr, err = x509.ParseCertificateRequest(byteCSR) // checkError(err) } else { csr, err = x509.ParseCertificateRequest(cert.Bytes) @@ -124,7 +124,7 @@ func ObtainAccountCertificate(w http.ResponseWriter, r *http.Request) { } } - err = certificates.VerifyRSASignatureFromCert(authCert, CSR, signature) + err = certs.VerifyRSASignatureFromCert(signedCert, byteCSR, signature) // TODO: AuthenticatorCertificate must be signed by CA, and for signed this account if err != nil { jsonResponse(w, err.Error(), http.StatusBadRequest) @@ -132,7 +132,7 @@ func ObtainAccountCertificate(w http.ResponseWriter, r *http.Request) { } // Add the certificate to the database - err = certificates.AddCert(pemCert, authCert) + err = certs.AddCert(signedCert, authCert) if err != nil { // AGAIN ASK FOR CLERIFICATION jsonResponse(w, fmt.Errorf("failed adding cert to certs table"), http.StatusInternalServerError) diff --git a/certs/rsa.go b/certs/rsa.go new file mode 100644 index 0000000..018491b --- /dev/null +++ b/certs/rsa.go @@ -0,0 +1,83 @@ +package certs + +import ( + "crypto" + "crypto/rsa" + "crypto/sha256" + "crypto/x509" + "encoding/base64" + "encoding/pem" + "errors" + "fmt" +) + +// VerifyRSASignatureFromCert verifies an RSA Signature (with SHA-256) for some string data +func VerifyRSASignatureFromCert(cert *x509.Certificate, message []byte, base64Signature string) error { + // get signature from base64 into bytes + signature, err := base64.StdEncoding.DecodeString(base64Signature) + + if err != nil { + return err + } + + // get message into bytes for hashing + hashed := sha256.Sum256(message) + + // get the public key from the certificate + key := cert.PublicKey + + // dot the actual verification of the signature + return rsa.VerifyPKCS1v15(key.(*rsa.PublicKey), crypto.SHA256, hashed[:], signature) +} + +// AddCert adds a new service certificate to the database, along with it's association to a device certificate +func AddCert(certString, deviceCert string) error { + + pemBlock, _ := pem.Decode([]byte(certString)) + if pemBlock == nil { + return errors.New("invalid PEM string for Certificate") + } + + // Parse and return pemBlock + certificate, err := x509.ParseCertificate(pemBlock.Bytes) + + if err != nil { + return errors.New("unable to convert certificate FromPem in AddNewCert") + } + + notBefore := certificate.NotBefore + expTime := certificate.NotAfter + + var pubKey []byte + if certificate != nil { + pubKey = pem.EncodeToMemory( + &pem.Block{ + Type: "RSA PUBLIC KEY", + Bytes: x509.MarshalPKCS1PublicKey(certificate.PublicKey.(*rsa.PublicKey)), + }, + ) + } else { + return errors.New("unable to obtain public key from cert in AddNewCert") + } + + // first try to update the certificate in the database + sql := "UPDATE certs SET cert=?, certExp=?, lastUpdated=? WHERE pubKey=?" + if _, err = db.Exec(sql, certString, expTime, notBefore, pubKey); err != nil { + fmt.Println("Error updating cert in cert table") + fmt.Println(err) + return errors.New("unable to update certificate in the database") + } + + // next insert into the database if the certificate wasn't previously stored + sql = "INSERT IGNORE certs(cert, eblob, certExp, pubKey, deviceCertId, lastUpdated)\n" + sql += "SELECT ?, ?, ?, ?, userDevice.id, ? FROM userDevice WHERE deviceCert=?" + + _, err = db.Exec(sql, certString, "a blob", expTime, pubKey, notBefore, deviceCert) + + if err != nil { + fmt.Println(err) + return errors.New("error inserting into cert table") + } + + return nil +} diff --git a/legacy/certificates/AccountIDVerifier.go b/legacy/certificates/AccountIDVerifier.go deleted file mode 100644 index 17e3302..0000000 --- a/legacy/certificates/AccountIDVerifier.go +++ /dev/null @@ -1,102 +0,0 @@ -package certificates - -import ( - "errors" - "fmt" -) - -// VerifyAccountID checks to see that accountID isn't taken by -// a different user -func VerifyAccountIDTaken(signedCert, username string) error { - // VERY SUPER IMPORANT, MYSQLTABLE ACCOUNTS NEEDS TO BE CREATED WITH ACCOUNTID (VARCHAR(100) NOT NULL and USERNAME) - - db := openDatabase() - defer db.Close() - - certificate, err := FromPem(signedCert) - if err != nil { - fmt.Println(err) - return errors.New("unable to convert certificate") - } - - // TODO: ASK ABOUT WHERE THE AUTHENTICATORS ARE PUTTING IN THE ACCOUNT ID - accountID := certificate.Subject.CommonName - - var exists int - - sql := "SELECT COUNT(1) FROM userDevice WHERE id=? AND username!=?" - - err = db.QueryRow(sql, accountID, username).Scan(&exists) - - if err != nil { - fmt.Println(err) - return errors.New("database error") - } - - if exists == 1 { - return errors.New("account already taken by other user") - } - - // pubKey, err := PubKeyFromCert(certificate) - // if err != nil { - // fmt.Println(err) - // return errors.New("Internal Server Error") - // } - // - // sql = "INSERT userDevice(deviceCert, username, revokeFlag, deviceExp, pubKey, name) VALUES (?, ?, ?, ?, ?)" - // _, err = db.Exec(sql, signedCert username, accountID, signedCert, string(pubKey), "depreicated") - - // if err != nil { - // fmt.Println(err) - // return errors.New("Database error") - // } - - return nil -} - -func VerifyAccountIDExists(signedCert, username string) error { - // VERY SUPER IMPORANT, MYSQLTABLE ACCOUNTS NEEDS TO BE CREATED WITH ACCOUNTID (VARCHAR(100) NOT NULL and USERNAME) - - db := openDatabase() - defer db.Close() - - certificate, err := FromPem(signedCert) - if err != nil { - fmt.Println(err) - return errors.New("unable to convert certificate") - } - - // TODO: ASK ABOUT WHERE THE AUTHENTICATORS ARE PUTTING IN THE ACCOUNT ID - accountID := certificate.Subject.CommonName - - var exists int - - sql := "SELECT COUNT(1) FROM userDevice WHERE id=? AND username=?" - - err = db.QueryRow(sql, accountID, username).Scan(&exists) - - if err != nil { - fmt.Println(err) - return errors.New("database error") - } - - if exists == 1 { - return errors.New("account already exists by current user") - } - - // pubKey, err := PubKeyFromCert(certificate) - // if err != nil { - // fmt.Println(err) - // return errors.New("Internal Server Error") - // } - // - // sql = "INSERT userDevice(deviceCert, username, revokeFlag, deviceExp, pubKey, name) VALUES (?, ?, ?, ?, ?)" - // _, err = db.Exec(sql, signedCert username, accountID, signedCert, string(pubKey), "depreicated") - - // if err != nil { - // fmt.Println(err) - // return errors.New("Database error") - // } - - return nil -} diff --git a/legacy/certificates/RSASignatureVerifier.go b/legacy/certificates/RSASignatureVerifier.go deleted file mode 100644 index 713d2ed..0000000 --- a/legacy/certificates/RSASignatureVerifier.go +++ /dev/null @@ -1,36 +0,0 @@ -package certificates - -import ( - "crypto" - "crypto/rsa" - "crypto/sha256" - "encoding/base64" - // "fmt" -) - -// VerifyRSASignatureFromCert verifies an RSA Signature (with SHA-256) for some string data -func VerifyRSASignatureFromCert(certWithKey, stringData, base64Signature string) error { - // get signature from base64 into bytes - signature, err := base64.StdEncoding.DecodeString(base64Signature) - - if err != nil { - return err - } - - // get message into bytes for hashing - message := []byte(stringData) - hashed := sha256.Sum256(message) - - // get the public key from the certificate - // parse the certificate - cert, err := FromPem(certWithKey) - - if err != nil { - return err - } - - key := cert.PublicKey - - // dot the actual verification of the signature - return rsa.VerifyPKCS1v15(key.(*rsa.PublicKey), crypto.SHA256, hashed[:], signature) -} diff --git a/legacy/certificates/certHelpers.go b/legacy/certificates/certHelpers.go deleted file mode 100644 index d878698..0000000 --- a/legacy/certificates/certHelpers.go +++ /dev/null @@ -1,169 +0,0 @@ -package certificates - -import ( - "crypto/rsa" - "crypto/x509" - "database/sql" - "encoding/gob" - "encoding/pem" - "errors" - "fmt" - "os" - "time" - - // This import is blank because that's how the documentation tells us how to use it - - "github.com/Usable-Security-and-Privacy-Lab/lets-auth-ca/util" - _ "github.com/go-sql-driver/mysql" - "gorm.io/driver/mysql" - "gorm.io/gorm" -) - -// TagDevice is the struct used to check authorization and ... (TODO ADD MROE INFO) -type TagDevice struct { - DeviceCert string `json:"devcieCert"` - Username string `json:"username"` - RevFlag string `json:"revokeFlag"` - DeviceExp string `json:"deviceExp"` -} - -// checkError does what it says -func checkError(err error) { - if err != nil { - fmt.Println("Fatal error ", err.Error()) - os.Exit(1) - } -} - -// FromPem takes in a PEM certificate (string) and returns an x.509 certificate pointer object -func FromPem(pemCert string) (*x509.Certificate, error) { - // Decode passed in pemCert to a block of data - pemBlock, _ := pem.Decode([]byte(pemCert)) - if pemBlock == nil { - return nil, errors.New("invalid PEM string for Certificate") - } - - // Parse and return pemBlock - return x509.ParseCertificate(pemBlock.Bytes) -} - -// PubKeyFromCert gets the public key bytes for the passed in certificate pointer (cert) -func PubKeyFromCert(cert *x509.Certificate) ([]byte, error) { - - var ( - pubKey []byte = nil // used to hold public key - err error = nil // used hold possible errors - ) - - if cert == nil { - err = errors.New("passed in a bad cert pointer") - } else { - pubKey = pem.EncodeToMemory( - &pem.Block{ - Type: "RSA PUBLIC KEY", - Bytes: x509.MarshalPKCS1PublicKey(cert.PublicKey.(*rsa.PublicKey)), - }, - ) - } - - return pubKey, err -} - -// openDatabase opens a sql database connection -func openDatabase() *sql.DB { - fmt.Println("Opening up old database (model...certHelpers)") - - cfg := util.GetConfig() - - // Open our database connection - temp_db, err := gorm.Open(mysql.Open(cfg.DbConfig), &gorm.Config{}) - if err != nil { - return nil - } - var sqlDB *sql.DB - sqlDB, err = temp_db.DB() - if err != nil { - return nil - } - - // Double Check if db connection is open - err = sqlDB.Ping() - if err != nil { - panic(err.Error()) - } - - return sqlDB -} - -// authNotRevoked checs to see if an authenticator certificate is in the database -// and not revoked (doesn't worry abou texpieration) -// POSIBILITY TO ADD ERROR HANDELING -func authNotRevoked(authCert string) bool { - - var ( - // Used to indicate if the certificate is invalid - revFlag string - // database connection - db = openDatabase() - // the return value - notRevoked = false - ) - - // Query the database for the given authCert - err := db.QueryRow("SELECT revokeFlag FROM userDevice WHERE deviceCert = ?", authCert).Scan(&revFlag) - if revFlag == "1" { // 1 indicates that the certificate is valid - notRevoked = true - } else if err != nil { - fmt.Println("Error in retrieving row for deviceCert in authNotRevoked().") - } else { - fmt.Println("Device revoked.") - } - return notRevoked - -} - -// authorize checs the database to make sure a device certificate is valid -// todo implement this puppy! -func authorize(deviceCert string) bool { - - db := openDatabase() - defer db.Close() - - deviceCertObject, err := FromPem(deviceCert) - if err != nil { - fmt.Println("Bad device cert PEM in authorize().") - return false - } - - if deviceCertObject.NotAfter.Before(time.Now()) { // notAfter is before now, cert is expired - fmt.Println("DeviceCert expiered in authorize()") - return false - } - - var tagOne TagDevice - err = db.QueryRow("SELECT username, revokeFlag FROM userDevice WHERE deviceCert = ?", deviceCert).Scan(&tagOne.Username, &tagOne.RevFlag) - - if err != nil { - fmt.Println("Authorization failed") - fmt.Println(err) - return false - } - - // if it's not associated with a username or has been revoked - if tagOne.Username == "" || tagOne.RevFlag == "0" { - fmt.Println("Device has been revoked or invalid account") - return false - } - - return true -} - -// loads a key from a GOB encoded file -func LoadKey(fileName string, key interface{}) { - inFile, err := os.Open(fileName) - checkError(err) - decoder := gob.NewDecoder(inFile) - err = decoder.Decode(key) - checkError(err) - inFile.Close() -} diff --git a/legacy/certificates/cert_test.go b/legacy/certificates/cert_test.go deleted file mode 100644 index 5237084..0000000 --- a/legacy/certificates/cert_test.go +++ /dev/null @@ -1,19 +0,0 @@ -package certificates - -import ( - "crypto/rsa" - "testing" - - "github.com/Usable-Security-and-Privacy-Lab/lets-auth-ca/util" -) - -func TestLoadKey(t *testing.T) { - cfg := util.GetConfig() - - var key rsa.PrivateKey - LoadKey(cfg.PrivateKeyFile, &key) - if key.Size() == 0 { - t.Fatalf("Error in loading private key") - } - -} diff --git a/legacy/certificates/recovery.go b/legacy/certificates/recovery.go deleted file mode 100644 index f520410..0000000 --- a/legacy/certificates/recovery.go +++ /dev/null @@ -1,203 +0,0 @@ -package certificates - -import ( - "errors" - "fmt" - "os/exec" - "time" -) - -var ( - databaseLocks = make(map[string]databaseLock) -) - -func CheckEtag(username, etag string) bool { - db := openDatabase() - defer db.Close() - - var tag string - sql := "SELECT etag FROM etags WHERE username=?" - err := db.QueryRow(sql, username).Scan(&tag) - if err != nil { - fmt.Println(err.Error()) - return false - } - - return etag == tag -} - -type databaseLock struct { - Username string - LockString string -} - -func RetrieveRecoveryDataEtag(authCertificate string) string { - - db := openDatabase() - defer db.Close() - - certificate, err := FromPem(authCertificate) - if err != nil { - return "unable to convert authcertPEM to certificate in RetrieveRecoveryDataEtag" - } - - pubKey, err := PubKeyFromCert(certificate) - if err != nil { - return "unable to get pubkey from certificate in RetrieveRecoveryDataEtag" - } - - var recoveryData string - sql := "SELECT eblob FROM certs WHERE pubKey=?" - err = db.QueryRow(sql, pubKey).Scan(&recoveryData) - - if err != nil { - fmt.Println(err.Error()) - return "unable to read eblob from certs in RetrieveRecoveryDataEtag" - } - - return recoveryData -} - -func RetrieveRecoveryData(authCertificate, username string) (string, string, error) { - - db := openDatabase() - defer db.Close() - - fmt.Println(authCertificate) - - err := verifyAuthCert(authCertificate, username) - if err != nil { - return "", "", errors.New("invalid auth cert passed into RetrievalRecovery") - } - - var recoveryData string - sql := "SELECT rdata FROM etags WHERE username=?" - err = db.QueryRow(sql, username).Scan(&recoveryData) - - if err != nil { - fmt.Println(err.Error()) - return "", "", errors.New("unable to read eblob from certs in RetrieveRecoveryData") - } - - // var etag string - // sql = "SELECT etag FROM etags WHERE username=?" - // err = db.QueryRow(sql, username).Scan(&etag) - - // if err != nil { - // fmt.Println(err.Error()) - // return "", "", errors.New("unable to read etag from etags in RetrieveRecoveryData") - // } - - return recoveryData, "etag", nil -} - -func PutRecoveryData(username, recoveryData string) (string, error) { - - db := openDatabase() - defer db.Close() - - // next insert into the database if the certificate wasn't previously stored - sql := "INSERT INTO etags(rdata, etag, username) VALUES (?, ?, ?)" - - _, err := db.Exec(sql, recoveryData, "etag", username) - if err != nil { - return "", err - } - - newEtag := updateEtag(username) - - return newEtag, nil -} - -func UpdateRecoveryData(authCertificate, username, recoveryData, lock string) (string, error) { - - db := openDatabase() - defer db.Close() - - err := verifyAuthCert(authCertificate, username) - if err != nil { - return "", errors.New("invalid auth cert") - } - - sql := "UPDATE etags SET rdata=? WHERE username=?" - _, err = db.Exec(sql, recoveryData, username) - if err != nil { - fmt.Println(err.Error()) - return "", errors.New("failed to update certificate") - } - - newEtag := updateEtag(username) - - return newEtag, nil -} - -func updateEtag(username string) string { - db := openDatabase() - defer db.Close() - - out, err := exec.Command("uuidgen").Output() - if err != nil { - fmt.Println(err.Error()) - return "" - } - - newEtag := string(out) - - sql := "UPDATE etags SET etag=? WHERE username=?" - _, err = db.Exec(sql, newEtag, username) - if err != nil { - fmt.Println(err.Error()) - return "" - } - - return newEtag -} - -func GetDatabaseLock(authCertificate, username string) (string, error) { - - err := verifyAuthCert(authCertificate, username) - if err != nil { - return "", errors.New("unable to convert authcertPEM to certificate in RetrieveRecoveryData") - } - - previousLock := databaseLocks[username] - if previousLock.Username != "" { - return "", errors.New("lock already exists") - } - - // generate the uuid - out, err := exec.Command("uuidgen").Output() - if err != nil { - fmt.Println(err.Error()) - return "", errors.New("unable to generate UUID") - } - - out = out[:len(out)-1] - - var lock databaseLock - lock.Username = username - lock.LockString = string(out) - - go lockDatabase(username, lock) - - return string(out), nil -} - -func lockDatabase(username string, lock databaseLock) { - - databaseLocks[username] = lock - - time.Sleep(30 * time.Second) - UnlockDatabase(username) - -} - -func UnlockDatabase(username string) { - - fmt.Println("Deleted lock from database") - storedLock := databaseLocks[username] - - if storedLock.LockString != "" && storedLock.Username == username { - delete(databaseLocks, username) - } -} diff --git a/legacy/certificates/revocation.go b/legacy/certificates/revocation.go deleted file mode 100644 index 76df99f..0000000 --- a/legacy/certificates/revocation.go +++ /dev/null @@ -1,21 +0,0 @@ -package certificates - -import ( - "errors" - "fmt" -) - -// RevokeAuthCertificate revokes an auth certificate in the database. -func RevokeAuthCertificate(authCert string) error { - db := openDatabase() - defer db.Close() - - sql := "UPDATE userDevice SET revokeFlag=0 where deviceCert=?" - _, err := db.Exec(sql, authCert) - if err != nil { - fmt.Println(err.Error()) - return errors.New("unable to update database") - } - - return nil -} diff --git a/legacy/certificates/x509.go b/legacy/certificates/x509.go deleted file mode 100644 index 463268d..0000000 --- a/legacy/certificates/x509.go +++ /dev/null @@ -1,279 +0,0 @@ -package certificates - -import ( - "crypto/rand" - "crypto/rsa" - "crypto/x509" - "crypto/x509/pkix" - "encoding/pem" - "errors" - "fmt" - "math/big" - "strconv" - "time" - - "github.com/Usable-Security-and-Privacy-Lab/lets-auth-ca/util" -) - -// CertMessage is used to pass websocket information -type CertMessage struct { - // Data used to identy websocket - Username string - - // Data shared between both - Certificate string `json:"certificate"` - Type string `json:"type"` - GoodUntil string `json:"goodUntil"` - PubKey string `json:"pubKey"` - Address string `json:"address"` - - // Site cert data - SavedData string `json:"savedData"` - - // Auth cert Data - AuthName string `json:"authName"` - RevFlag string `json:"revFlag"` -} - -// SignCert takes in a certificate and signs the certificate. -// Requires: Data (byte array), deviceCert (string), activeDays (string), checkDeviceCert (bool) -// Ensures: The passed in certificate (data) is signed -// Errors: Invalid Device Certificate, Invalid Date, Internal Server Error -func SignCert(data []byte, deviceCert, activeDays string, checkDeviceCert bool) (string, bool) { - - // don't check the device cert if this is for a device cert - if checkDeviceCert { - if valid := authorize(deviceCert); !valid { - return "Invalid Device Cert", false - } - } - - //get the cert out of the PEM string - cert, _ := pem.Decode(data) - var csr *x509.CertificateRequest - var err error - // var err error - if cert == nil { - csr, err = x509.ParseCertificateRequest(data) - // checkError(err) - } else { - csr, err = x509.ParseCertificateRequest(cert.Bytes) - // checkError(err) - } - - if err != nil { - return "Internal Server Error", false - } - - random := rand.Reader - - cfg := util.GetConfig() - - var key rsa.PrivateKey - LoadKey(cfg.PrivateKeyFile, &key) - - now := time.Now() - - validDays, err := strconv.Atoi(activeDays) - if err != nil { - fmt.Println("Invalid date passed to SignCert: ", activeDays) - return "Internal Server Error", false - } - - // NOTE A PREVIOUS VERSION HAD SITE CERTS VALID FOR 10 DAYS AND DEVICE CERTS VALID FOR 7 DAYS - then := now.Add(time.Duration(60 * 60 * 24 * validDays * 1000 * 1000 * 1000)) - - LAnow := time.Now() - LAthen := now.Add(60 * 60 * 24 * 365 * 1000 * 1000 * 1000) // one year (365 days) for CA cert - - //template read in from the CSR - template := x509.Certificate{ - SerialNumber: big.NewInt(1), - Subject: pkix.Name{ - CommonName: csr.Subject.CommonName, - }, - - EmailAddresses: csr.EmailAddresses, - NotBefore: now, - NotAfter: then, - - SubjectKeyId: []byte{1, 2, 3, 4}, - KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, - BasicConstraintsValid: true, - IsCA: false, - } - - //template of the CA's certificate - letsauth := x509.Certificate{ - SerialNumber: big.NewInt(1), - Subject: pkix.Name{ - CommonName: "letsauth.org", - Organization: []string{"Let's Authenticate"}, - }, - - EmailAddresses: []string{""}, - NotBefore: LAnow, - NotAfter: LAthen, - - SubjectKeyId: []byte{1, 2, 3, 4}, - KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, - - BasicConstraintsValid: true, - IsCA: true, - } - - signed := letsauth - - // actually sign the certificate with the parent certificate's key - derBytes2, err := x509.CreateCertificate(random, &template, &signed, csr.PublicKey, &key) - checkError(err) - // shouldn't we just return an internal server error if we fail? - - certString := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes2}) - - return string(certString), true - -} - -// AddAuthCert adds an authenticator certificate to the database, linking it to the username -// returns whether or not it worked, and a potential error string -func AddAuthCert(username, authCert string) (bool, string) { - db := openDatabase() - defer db.Close() - - // fmt.Println(authCert) - - // pull necessary data from the authenticator certificate - certificate, err := FromPem(authCert) - if err != nil { - return false, "Internal Server Error" - } - expTime := certificate.NotAfter - pubKey, err := PubKeyFromCert(certificate) - if err != nil { - return false, "Internal Server Error" - } - - name := certificate.Subject.CommonName - - var exists int - // want to know if there's an authenticator with the same name for the same user with a different public key - fmt.Println("Name:", name) - fmt.Println("PubKey:", pubKey) - fmt.Println("Username:", username) - err = db.QueryRow("SELECT EXISTS(SELECT 1 FROM userDevice WHERE name=? AND pubKey!=? AND username=?)", name, pubKey, username).Scan(&exists) - fmt.Println("exists?", exists) - - if err != nil { // it already exists - return false, "Internal Server Error" - } else if exists == 1 { - return false, "Duplicate authenticator name: please choose another name." - } - - // this will update it if possible - fmt.Println("AuthCert:", authCert) - fmt.Println("PubKey:", pubKey) - fmt.Println("ExpTime:", expTime) - _, err = db.Exec("UPDATE userDevice SET deviceCert=?, revokeFlag=1, deviceExp=? WHERE pubKey=?", authCert, expTime, pubKey) - - if err != nil { - fmt.Println("Error in updating authenticator certificate: ", err.Error()) - return false, "Internal Server Error" - } - - // try to add the entry in case it wasn't updated (because it didn't exist) - // if there's already a cert in the database for the given public key, this won't add anything - fmt.Println("DeviceCert:", authCert) - fmt.Println("username:", username) - fmt.Println("revokeFlag:", 1) - fmt.Println("deviceExp:", expTime) - fmt.Println("pubKey:", pubKey) - fmt.Println("name:", name) - temp, err2 := db.Exec("INSERT IGNORE userDevice(deviceCert, username, revokeFlag, deviceExp, pubKey, name) VALUES (?, ?, ?, ?, ?, ?)", authCert, username, 1, expTime, pubKey, name) - fmt.Println(temp.RowsAffected()) - - if err2 != nil { - fmt.Println("Error", err2) - fmt.Println("Length of deviceCert: ", len(authCert)) - return false, "Internal Server Error" - } - - // add the device cert to the certificate table - fmt.Println("Before adding cert to certs table.##############################") - err = AddCert(authCert, authCert) - fmt.Println("After adding cert to certs table.##############################") - - if err != nil { - fmt.Println("Error", err) - fmt.Println("Length of deviceCert: ", len(authCert)) - return false, "Internal Server Error" - } - - return true, "" -} - -// AddCert adds a new service certificate to the database, along with it's association to a device certificate -func AddCert(certString, deviceCert string) error { - - db := openDatabase() - defer db.Close() - - certificate, err := FromPem(certString) - if err != nil { - return errors.New("unable to convert certificate FromPem in AddNewCert") - } - - notBefore := certificate.NotBefore - expTime := certificate.NotAfter - pubKey, err := PubKeyFromCert(certificate) - if err != nil { - return errors.New("unable to obtain public key from cert in AddNewCert") - } - - // first try to update the certificate in the database - sql := "UPDATE certs SET cert=?, certExp=?, lastUpdated=? WHERE pubKey=?" - if _, err = db.Exec(sql, certString, expTime, notBefore, pubKey); err != nil { - fmt.Println("Error updating cert in cert table") - fmt.Println(err) - return errors.New("unable to update certificate in the database") - } - - // next insert into the database if the certificate wasn't previously stored - sql = "INSERT IGNORE certs(cert, eblob, certExp, pubKey, deviceCertId, lastUpdated)\n" - sql += "SELECT ?, ?, ?, ?, userDevice.id, ? FROM userDevice WHERE deviceCert=?" - - _, err = db.Exec(sql, certString, "a blob", expTime, pubKey, notBefore, deviceCert) - - if err != nil { - fmt.Println(err) - return errors.New("error inserting into cert table") - } - - return nil -} - -func verifyAuthCert(authCert, username string) error { - fmt.Println("Verifying Auth Cert") - fmt.Println(username) - fmt.Println(authCert) - - db := openDatabase() - defer db.Close() - - var exists int - // want to know if there's an authenticator with the same name for the same user with a different public key - err := db.QueryRow("SELECT EXISTS(SELECT 1 FROM userDevice WHERE deviceCert=? AND username=?)", authCert, username).Scan(&exists) - fmt.Println("Exists:", exists) - - if err != nil { // it already exists - fmt.Println("Internal Error") - return errors.New("internal server error") - } else if exists == 1 { - fmt.Println("Cert Verified") - return nil - } else { - fmt.Println("Doesn't exist") - return errors.New("AuthCert Doesn't Exist") - } -} diff --git a/legacy/database/fidoDB.go b/legacy/database/fidoDB.go deleted file mode 100644 index 96b8105..0000000 --- a/legacy/database/fidoDB.go +++ /dev/null @@ -1,46 +0,0 @@ -package database - -import ( - "database/sql" - "fmt" - "os" - - // This import is blank because that's how the documentation tells us how to use it - _ "github.com/go-sql-driver/mysql" -) - -// For information on how the secret is loaded into the docker container at runtime, -// see the DockerUpdate.md file in root. - -// fidoDBpass format: fido_user:[password]@tcp(localhost:3306)/fido - -var FidoDBstr string - -// openDatabase opens a sql database connection -func InitFidoDatabase() bool { - dat, err := os.ReadFile("/run/secrets/fidoDBpass") - if err != nil { - fmt.Println(err.Error()) - return false - } - FidoDBstr = string(dat) - - fmt.Println("Opening up fido database (fido test)") - - // Open db connection - db, err := sql.Open("mysql", FidoDBstr) - if err != nil { - fmt.Println(err.Error()) - return false - } - defer db.Close() - - // Double Check if db connection is open - err = db.Ping() - if err != nil { - fmt.Println(err.Error()) - return false - } - - return true -} diff --git a/legacy/database/passDB.go b/legacy/database/passDB.go deleted file mode 100644 index 2d3f5b2..0000000 --- a/legacy/database/passDB.go +++ /dev/null @@ -1,46 +0,0 @@ -package database - -import ( - "database/sql" - "fmt" - "os" - - // This import is blank because that's how the documentation tells us how to use it - _ "github.com/go-sql-driver/mysql" -) - -// For information on how the secret is loaded into the docker container at runtime, -// see the DockerUpdate.md file in root. - -// passDBpass format: letsauthtest:[password]@tcp(localhost:3306)/lets_auth - -var PassDBstr string - -// openDatabase opens a sql database connection -func InitPassDatabase() bool { - dat, err := os.ReadFile("/run/secrets/passDBpass") - if err != nil { - fmt.Println(err.Error()) - return false - } - PassDBstr = string(dat) - - fmt.Println("Opening up database (password test)") - - // Open db connection - db, err := sql.Open("mysql", PassDBstr) - if err != nil { - fmt.Println(err.Error()) - return false - } - defer db.Close() - - // Double Check if db connection is open - err = db.Ping() - if err != nil { - fmt.Println(err.Error()) - return false - } - - return true -} From 4205715be813acb28cf98193a391b155edabb6d6 Mon Sep 17 00:00:00 2001 From: 10808249 Date: Tue, 15 Nov 2022 12:09:14 -0700 Subject: [PATCH 14/14] additional fix for service --- api/{username.go => user.go} | 17 ++++++++++------- certs/rsa.go | 8 ++++++-- main.go | 2 +- models/authkeys.go | 9 +++++---- 4 files changed, 22 insertions(+), 14 deletions(-) rename api/{username.go => user.go} (93%) diff --git a/api/username.go b/api/user.go similarity index 93% rename from api/username.go rename to api/user.go index b52b277..b60e881 100644 --- a/api/username.go +++ b/api/user.go @@ -131,13 +131,16 @@ func ObtainAccountCertificate(w http.ResponseWriter, r *http.Request) { return } - // Add the certificate to the database - err = certs.AddCert(signedCert, authCert) - if err != nil { - // AGAIN ASK FOR CLERIFICATION - jsonResponse(w, fmt.Errorf("failed adding cert to certs table"), http.StatusInternalServerError) - return - } + /* + // Maybe we don't need this statement + // Add the certificate to the database + err = certs.AddCert(signedCert, authCert) + if err != nil { + // AGAIN ASK FOR CLERIFICATION + jsonResponse(w, fmt.Errorf("failed adding cert to certs table"), http.StatusInternalServerError) + return + } + */ response := serviceResponse{SignedCertificate: pemCert} final, _ := json.Marshal(response) diff --git a/certs/rsa.go b/certs/rsa.go index 018491b..3de408a 100644 --- a/certs/rsa.go +++ b/certs/rsa.go @@ -9,6 +9,8 @@ import ( "encoding/pem" "errors" "fmt" + + "gorm.io/gorm" ) // VerifyRSASignatureFromCert verifies an RSA Signature (with SHA-256) for some string data @@ -60,9 +62,11 @@ func AddCert(certString, deviceCert string) error { return errors.New("unable to obtain public key from cert in AddNewCert") } + var db gorm.DB + // first try to update the certificate in the database sql := "UPDATE certs SET cert=?, certExp=?, lastUpdated=? WHERE pubKey=?" - if _, err = db.Exec(sql, certString, expTime, notBefore, pubKey); err != nil { + if err = db.Exec(sql, certString, expTime, notBefore, pubKey).Error; err != nil { fmt.Println("Error updating cert in cert table") fmt.Println(err) return errors.New("unable to update certificate in the database") @@ -72,7 +76,7 @@ func AddCert(certString, deviceCert string) error { sql = "INSERT IGNORE certs(cert, eblob, certExp, pubKey, deviceCertId, lastUpdated)\n" sql += "SELECT ?, ?, ?, ?, userDevice.id, ? FROM userDevice WHERE deviceCert=?" - _, err = db.Exec(sql, certString, "a blob", expTime, pubKey, notBefore, deviceCert) + err = db.Exec(sql, certString, "a blob", expTime, pubKey, notBefore, deviceCert).Error if err != nil { fmt.Println(err) diff --git a/main.go b/main.go index 5bf4386..12c231b 100644 --- a/main.go +++ b/main.go @@ -58,7 +58,7 @@ func main() { router.HandleFunc("/la3/account/create-begin/{username}", api.CreateBegin).Methods("GET") 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/user/{username}/account", api.ObtainAccountCertificate) + router.HandleFunc("/la3/user/{username}/account", api.ObtainAccountCertificate).Methods("POST") url := fmt.Sprintf("%s:%d", cfg.Host, cfg.Port) diff --git a/models/authkeys.go b/models/authkeys.go index dec56f2..02f5e78 100644 --- a/models/authkeys.go +++ b/models/authkeys.go @@ -2,6 +2,7 @@ package models import ( "strings" + "gorm.io/gorm" ) @@ -12,7 +13,7 @@ import ( type AuthKey struct { gorm.Model - Key string + Key string UserID uint } @@ -31,9 +32,9 @@ func GetAuthKeysForUser(user User) ([]AuthKey, error) { return authKeys, err } -func AuthKeyPresent(key string, authKeys []AuthKey) (bool) { +func AuthKeyPresent(key string, authKeys []AuthKey) bool { for i := 0; i < len(authKeys); i++ { - if authKeys[i].Key == key { + if authKeys[i].Key == key { return true } } @@ -44,4 +45,4 @@ func AuthKeyPresent(key string, authKeys []AuthKey) (bool) { // after they have logged in (so at the finish part of a FIDO2 login). func DeleteAuthKey(key string) error { return db.Where("key = ?", key).Delete(&AuthKey{}).Error -} \ No newline at end of file +}