diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e4e1da89..6751756e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -10,7 +10,6 @@ repos: - id: trailing-whitespace exclude: jsonschema/api-docs.md - id: detect-private-key - exclude: 'pkg/tokens/testdata/.*' - repo: https://github.com/google/yamlfmt rev: v0.13.0 hooks: diff --git a/.typos.toml b/.typos.toml index 52a69c09..0d0b596c 100644 --- a/.typos.toml +++ b/.typos.toml @@ -1,5 +1,5 @@ [files] -extend-exclude = ["db","internal/ent/generated/**","go.mod","go.sum","pkg/testutils/","pkg/passwd/","pkg/tokens/testdata/","pkg/tokens/expires_test.go","internal/graphapi/tools_test.go","internal/httpserve/handlers/tools_test.go","pkg/keygen/auth_test.go","pkg/utils/oas/","pkg/keygen/crypto_test.go","pkg/auth/auth_test.go"] +extend-exclude = ["db","internal/ent/generated/**","go.mod","go.sum","pkg/testutils/","internal/graphapi/tools_test.go","internal/httpserve/handlers/tools_test.go"] ignore-hidden = true ignore-files = true ignore-dot = true @@ -17,4 +17,4 @@ identifier-leading-digits = false locale = "en" extend-ignore-identifiers-re = [] extend-ignore-words-re = ["(?i)requestor","(?i)indentity","(?i)encrypter","(?i)seeked","(?i)generater"] -extend-ignore-re = ["#\\s*spellchecker:off\\s*\\n.*\\n\\s*#\\s*spellchecker:on"] \ No newline at end of file +extend-ignore-re = ["(?Rm)^.*//\\s*spellchecker:disable-line$"] \ No newline at end of file diff --git a/go.mod b/go.mod index 116bd399..cfe1992e 100644 --- a/go.mod +++ b/go.mod @@ -60,8 +60,8 @@ require ( github.com/theopenlane/entx v0.1.4 github.com/theopenlane/gqlgen-plugins v0.1.0 github.com/theopenlane/httpsling v0.1.0 - github.com/theopenlane/iam v0.1.5 - github.com/theopenlane/utils v0.1.3 + github.com/theopenlane/iam v0.1.6 + github.com/theopenlane/utils v0.1.4 github.com/tursodatabase/libsql-client-go v0.0.0-20240812094001-348a4e45b535 github.com/vektah/gqlparser/v2 v2.5.16 github.com/wundergraph/graphql-go-tools v1.67.4 diff --git a/go.sum b/go.sum index 323d7380..4686a4b3 100644 --- a/go.sum +++ b/go.sum @@ -548,10 +548,10 @@ github.com/theopenlane/gqlgen-plugins v0.1.0 h1:DL3knGQ+pcoXceSFvmgWzzlgIyPRgl71 github.com/theopenlane/gqlgen-plugins v0.1.0/go.mod h1:sP0nCjrE3KoVT4qfYqMrSK1XZa3Bx2oTIRSdEF7F5l0= github.com/theopenlane/httpsling v0.1.0 h1:IHWUSo213stJTmHOHjNIg5b3npgpchzMdPMY7jAkimI= github.com/theopenlane/httpsling v0.1.0/go.mod h1:wOyNfO4moIbmP4stQc9Kasgp+Q4sODo3LOLwvjUe/PA= -github.com/theopenlane/iam v0.1.5 h1:CreDFrl8hLTpdQJcs/gFbkk8EuOFClGCClIpCUxPrOg= -github.com/theopenlane/iam v0.1.5/go.mod h1:KZd/k1nEHSzS9wQeQHLqRabJuH3XkF5g8aGqzRhjSmQ= -github.com/theopenlane/utils v0.1.3 h1:ankd7gtxOMC1xvRike13+Q2oI7paBSKu9Z8fUujdd50= -github.com/theopenlane/utils v0.1.3/go.mod h1:gX64mPxANzaIc1GCgtAueWGDZVsKXiMyQF9oxTrDdVg= +github.com/theopenlane/iam v0.1.6 h1:ps6xLXHpnGy687uLPRZiD7034DRVqaWEfJLCJVMx95o= +github.com/theopenlane/iam v0.1.6/go.mod h1:mOtYjuqUD7SX4EkwXFAYwf8+mwPDsRvTsLhAngqVIxM= +github.com/theopenlane/utils v0.1.4 h1:G2hrRBOugS2VvdqumBMrp2uSjulwbJMt8QO0EpwKm7g= +github.com/theopenlane/utils v0.1.4/go.mod h1:aFHkhJKNMGcaqNSfWV+hClZeejjuz4+oerrM7orq0Jw= github.com/tursodatabase/libsql-client-go v0.0.0-20240812094001-348a4e45b535 h1:iLjJLq2A5J6L9zrhyNn+fpmxFvtEpYB4XLMr0rX3epI= github.com/tursodatabase/libsql-client-go v0.0.0-20240812094001-348a4e45b535/go.mod h1:l8xTsYB90uaVdMHXMCxKKLSgw5wLYBwBKKefNIUnm9s= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= diff --git a/internal/ent/hooks/user.go b/internal/ent/hooks/user.go index 8fe5ffaf..200af93b 100644 --- a/internal/ent/hooks/user.go +++ b/internal/ent/hooks/user.go @@ -18,7 +18,7 @@ import ( "github.com/theopenlane/core/internal/ent/generated/organization" "github.com/theopenlane/core/internal/ent/generated/privacy" "github.com/theopenlane/core/pkg/enums" - "github.com/theopenlane/core/pkg/passwd" + "github.com/theopenlane/utils/passwd" ) const ( diff --git a/internal/httpserve/handlers/login.go b/internal/httpserve/handlers/login.go index e964f67e..85bf03bf 100644 --- a/internal/httpserve/handlers/login.go +++ b/internal/httpserve/handlers/login.go @@ -13,7 +13,7 @@ import ( "github.com/theopenlane/core/pkg/enums" "github.com/theopenlane/core/pkg/models" - "github.com/theopenlane/core/pkg/passwd" + "github.com/theopenlane/utils/passwd" ) // LoginHandler validates the user credentials and returns a valid cookie diff --git a/internal/httpserve/handlers/register.go b/internal/httpserve/handlers/register.go index 39c2cb98..46f80abc 100644 --- a/internal/httpserve/handlers/register.go +++ b/internal/httpserve/handlers/register.go @@ -150,7 +150,7 @@ func (h *Handler) storeAndSendEmailVerificationToken(ctx context.Context, user * // BindRegisterHandler is used to bind the register endpoint to the OpenAPI schema func (h *Handler) BindRegisterHandler() *openapi3.Operation { register := openapi3.NewOperation() - register.Description = "Register creates a new user in the database with the specified password, allowing the user to login to OpenLane. This endpoint requires a 'strong' password and a valid register request, otherwise a 400 reply is returned. The password is stored in the database as an argon2 derived key so it is impossible for a hacker to get access to raw passwords. A personal organization is created for the user registering based on the organization data in the register request and the user is assigned the Owner role" + register.Description = "Register creates a new user in the database with the specified password, allowing the user to login to Openlane. This endpoint requires a 'strong' password and a valid register request, otherwise a 400 reply is returned. The password is stored in the database as an argon2 derived key so it is impossible for a hacker to get access to raw passwords. A personal organization is created for the user registering based on the organization data in the register request and the user is assigned the Owner role" register.OperationID = "RegisterHandler" register.Security = &openapi3.SecurityRequirements{} diff --git a/internal/httpserve/handlers/resetpassword.go b/internal/httpserve/handlers/resetpassword.go index 88c61a69..51c9a09a 100644 --- a/internal/httpserve/handlers/resetpassword.go +++ b/internal/httpserve/handlers/resetpassword.go @@ -22,7 +22,7 @@ import ( "github.com/theopenlane/core/internal/ent/generated" "github.com/theopenlane/core/internal/ent/privacy/token" "github.com/theopenlane/core/pkg/models" - "github.com/theopenlane/core/pkg/passwd" + "github.com/theopenlane/utils/passwd" ) // ResetPassword allows the user (after requesting a password reset) to diff --git a/internal/httpserve/server/openapi.go b/internal/httpserve/server/openapi.go index 31be4a8c..4b00c1d0 100644 --- a/internal/httpserve/server/openapi.go +++ b/internal/httpserve/server/openapi.go @@ -114,16 +114,16 @@ func NewOpenAPISpec() (*openapi3.T, error) { Paths: openapi3.NewPaths(), Servers: openapi3.Servers{ &openapi3.Server{ - Description: "OpenLane API Server", + Description: "Openlane API Server", URL: "https://api.theopenlane.io/v1", }, &openapi3.Server{ - Description: "OpenLane API Server (local)", + Description: "Openlane API Server (local)", URL: "http://localhost:17608/v1", }, }, ExternalDocs: &openapi3.ExternalDocs{ - Description: "Documentation for OpenLane's API services", + Description: "Documentation for Openlane's API services", URL: "https://docs.theopenlane.io", }, diff --git a/pkg/keygen/crypto.go b/pkg/keygen/crypto.go deleted file mode 100644 index 721742dc..00000000 --- a/pkg/keygen/crypto.go +++ /dev/null @@ -1,56 +0,0 @@ -package keygen - -import ( - "crypto/hmac" - "crypto/sha256" - "crypto/sha512" - "crypto/subtle" - "encoding/base64" - "fmt" - "strings" -) - -// S256Challenge creates [RFC 7636]: https://datatracker.ietf.org/doc/html/rfc7636#section-4.2 -func S256Challenge(code string) string { - h := sha256.New() - h.Write([]byte(code)) - - return strings.TrimRight(base64.URLEncoding.EncodeToString(h.Sum(nil)), "=") -} - -// SHA256 creates sha256 hash as defined in FIPS 180-4 -func SHA256(text string) string { - h := sha256.New() - h.Write([]byte(text)) - - return fmt.Sprintf("%x", h.Sum(nil)) -} - -// SHA512 creates sha512 hash as defined in FIPS 180-4 -func SHA512(text string) string { - h := sha512.New() - h.Write([]byte(text)) - - return fmt.Sprintf("%x", h.Sum(nil)) -} - -// HS256 creates a HMAC hash with sha256 digest algorithm. -func HS256(text string, secret string) string { - h := hmac.New(sha256.New, []byte(secret)) - h.Write([]byte(text)) - - return fmt.Sprintf("%x", h.Sum(nil)) -} - -// HS512 creates a HMAC hash with sha512 digest algorithm -func HS512(text string, secret string) string { - h := hmac.New(sha512.New, []byte(secret)) - h.Write([]byte(text)) - - return fmt.Sprintf("%x", h.Sum(nil)) -} - -// Equal compares two hash strings for equality without leaking timing information -func Equal(hash1 string, hash2 string) bool { - return subtle.ConstantTimeCompare([]byte(hash1), []byte(hash2)) == 1 -} diff --git a/pkg/keygen/crypto_test.go b/pkg/keygen/crypto_test.go deleted file mode 100644 index 39c1e085..00000000 --- a/pkg/keygen/crypto_test.go +++ /dev/null @@ -1,136 +0,0 @@ -package keygen_test - -import ( - "fmt" - "testing" - - "github.com/theopenlane/core/pkg/keygen" -) - -func TestS256Challenge(t *testing.T) { - scenarios := []struct { - code string - expected string - }{ - {"", "47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU"}, - {"123", "pmWkWSBCL51Bfkhn79xPuKBKHz__H6B-mY6G9_eieuM"}, - } - - for _, s := range scenarios { - t.Run(s.code, func(t *testing.T) { - result := keygen.S256Challenge(s.code) - - if result != s.expected { - t.Fatalf("Expected %q, got %q", s.expected, result) - } - }) - } -} - -func TestSHA256(t *testing.T) { - scenarios := []struct { - code string - expected string - }{ - {"", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"}, - {"123", "a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3"}, - } - - for _, s := range scenarios { - t.Run(s.code, func(t *testing.T) { - result := keygen.SHA256(s.code) - - if result != s.expected { - t.Fatalf("Expected %v, got %v", s.expected, result) - } - }) - } -} - -func TestSHA512(t *testing.T) { - scenarios := []struct { - code string - expected string - }{ - {"", "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"}, - {"123", "3c9909afec25354d551dae21590bb26e38d53f2173b8d3dc3eee4c047e7ab1c1eb8b85103e3be7ba613b31bb5c9c36214dc9f14a42fd7a2fdb84856bca5c44c2"}, - } - - for _, s := range scenarios { - t.Run(s.code, func(t *testing.T) { - result := keygen.SHA512(s.code) - - if result != s.expected { - t.Fatalf("Expected %v, got %v", s.expected, result) - } - }) - } -} - -func TestHS256(t *testing.T) { - scenarios := []struct { - text string - secret string - expected string - }{ - {" ", "test", "9fb4e4a12d50728683a222b4fc466a69ee977332cfcdd6b9ebb44c7121dbd99f"}, - {" ", "test2", "d792417a504716e22805d940125ec12e68e8cb18fc84674703bd96c59f1e1228"}, - {"hello", "test", "f151ea24bda91a18e89b8bb5793ef324b2a02133cce15a28a719acbd2e58a986"}, - {"hello", "test2", "16436e8dcbf3d7b5b0455573b27e6372699beb5bfe94e6a2a371b14b4ae068f4"}, - } - - for i, s := range scenarios { - t.Run(fmt.Sprintf("%d-%s", i, s.text), func(t *testing.T) { - result := keygen.HS256(s.text, s.secret) - - if result != s.expected { - t.Fatalf("Expected \n%v, \ngot \n%v", s.expected, result) - } - }) - } -} - -func TestHS512(t *testing.T) { - scenarios := []struct { - text string - secret string - expected string - }{ - {" ", "test", "eb3bdb0352c95c38880c1f645fc7e1d1332644f938f50de0d73876e42d6f302e599bb526531ba79940e8b314369aaef3675322d8d851f9fc6ea9ed121286d196"}, - {" ", "test2", "8b69e84e9252af78ae8b1c4bed3c9f737f69a3df33064cfbefe76b36d19d1827285e543cdf066cdc8bd556cc0cd0e212d52e9c12a50cd16046181ff127f4cf7f"}, - {"hello", "test", "44f280e11103e295c26cd61dd1cdd8178b531b860466867c13b1c37a26b6389f8af110efbe0bb0717b9d9c87f6fe1c97b3b1690936578890e5669abf279fe7fd"}, - {"hello", "test2", "d7f10b1b66941b20817689b973ca9dfc971090e28cfb8becbddd6824569b323eca6a0cdf2c387aa41e15040007dca5a011dd4e4bb61cfd5011aa7354d866f6ef"}, - } - - for i, s := range scenarios { - t.Run(fmt.Sprintf("%d-%q", i, s.text), func(t *testing.T) { - result := keygen.HS512(s.text, s.secret) - - if result != s.expected { - t.Fatalf("Expected \n%v, \ngot \n%v", s.expected, result) - } - }) - } -} - -func TestEqual(t *testing.T) { - scenarios := []struct { - hash1 string - hash2 string - expected bool - }{ - {"", "", true}, - {"abc", "abd", false}, // # spellcheck: off - {"abc", "abc", true}, - } - - for _, s := range scenarios { - t.Run(fmt.Sprintf("%qVS%q", s.hash1, s.hash2), func(t *testing.T) { - result := keygen.Equal(s.hash1, s.hash2) - - if result != s.expected { - t.Fatalf("Expected %v, got %v", s.expected, result) - } - }) - } -} diff --git a/pkg/keygen/doc.go b/pkg/keygen/doc.go deleted file mode 100644 index c683bea6..00000000 --- a/pkg/keygen/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// Package keygen provides functionality for generating API client IDs and secrets -package keygen diff --git a/pkg/keygen/encrypt.go b/pkg/keygen/encrypt.go deleted file mode 100644 index 0ccf9105..00000000 --- a/pkg/keygen/encrypt.go +++ /dev/null @@ -1,59 +0,0 @@ -package keygen - -import ( - "crypto/aes" - "crypto/cipher" - crand "crypto/rand" - "encoding/base64" - "io" -) - -// Encrypt encrypts data with key (must be valid 32 char aes key). -func Encrypt(data []byte, key string) (string, error) { - block, err := aes.NewCipher([]byte(key)) - if err != nil { - return "", err - } - - gcm, err := cipher.NewGCM(block) - if err != nil { - return "", err - } - - nonce := make([]byte, gcm.NonceSize()) - - // populates the nonce with a cryptographically secure random sequence - if _, err := io.ReadFull(crand.Reader, nonce); err != nil { - return "", err - } - - cipherByte := gcm.Seal(nonce, nonce, data, nil) - - result := base64.StdEncoding.EncodeToString(cipherByte) - - return result, nil -} - -// Decrypt decrypts encrypted text with key (must be valid 32 chars aes key). -func Decrypt(cipherText string, key string) ([]byte, error) { - block, err := aes.NewCipher([]byte(key)) - if err != nil { - return nil, err - } - - gcm, err := cipher.NewGCM(block) - if err != nil { - return nil, err - } - - nonceSize := gcm.NonceSize() - - cipherByte, err := base64.StdEncoding.DecodeString(cipherText) - if err != nil { - return nil, err - } - - nonce, cipherByteClean := cipherByte[:nonceSize], cipherByte[nonceSize:] - - return gcm.Open(nil, nonce, cipherByteClean, nil) -} diff --git a/pkg/keygen/encrypt_test.go b/pkg/keygen/encrypt_test.go deleted file mode 100644 index 2dd0ec80..00000000 --- a/pkg/keygen/encrypt_test.go +++ /dev/null @@ -1,79 +0,0 @@ -package keygen_test - -import ( - "testing" - - "github.com/theopenlane/core/pkg/keygen" -) - -func TestEncrypt(t *testing.T) { - scenarios := []struct { - data string - key string - expectError bool - }{ - {"", "", true}, - {"123", "test", true}, // key must be valid 32 char aes string - {"123", "abcdabcdabcdabcdabcdabcdabcdabcd", false}, - } - - for i, scenario := range scenarios { - result, err := keygen.Encrypt([]byte(scenario.data), scenario.key) - - if scenario.expectError && err == nil { - t.Errorf("(%d) Expected error got nil", i) - } - - if !scenario.expectError && err != nil { - t.Errorf("(%d) Expected nil got error %v", i, err) - } - - if scenario.expectError && result != "" { - t.Errorf("(%d) Expected empty string, got %q", i, result) - } - - if !scenario.expectError && result == "" { - t.Errorf("(%d) Expected non empty encrypted result string", i) - } - - // try to decrypt - if result != "" { - decrypted, _ := keygen.Decrypt(result, scenario.key) - if string(decrypted) != scenario.data { - t.Errorf("(%d) Expected decrypted value to match with the data input, got %q", i, decrypted) - } - } - } -} - -func TestDecrypt(t *testing.T) { - scenarios := []struct { - cipher string - key string - expectError bool - expectedData string - }{ - {"", "", true, ""}, - {"123", "test", true, ""}, // key must be valid 32 char aes string - {"8kcEqilvvYKYcfnSr0aSC54gmnQCsB02SaB8ATlnA==", "abcdabcdabcdabcdabcdabcdabcdabcd", true, ""}, // illegal base64 encoded cipherText - {"8kcEqilvv+YKYcfnSr0aSC54gmnQCsB02SaB8ATlnA==", "abcdabcdabcdabcdabcdabcdabcdabcd", false, "123"}, - } - - for i, scenario := range scenarios { - result, err := keygen.Decrypt(scenario.cipher, scenario.key) - - if scenario.expectError && err == nil { - t.Errorf("(%d) Expected error got nil", i) - } - - if !scenario.expectError && err != nil { - t.Errorf("(%d) Expected nil got error %v", i, err) - } - - resultStr := string(result) - - if resultStr != scenario.expectedData { - t.Errorf("(%d) Expected %q, got %q", i, scenario.expectedData, resultStr) - } - } -} diff --git a/pkg/keygen/keygen.go b/pkg/keygen/keygen.go deleted file mode 100644 index 77f6fdba..00000000 --- a/pkg/keygen/keygen.go +++ /dev/null @@ -1,143 +0,0 @@ -package keygen - -import ( - "crypto/hmac" - "crypto/rand" - "crypto/sha256" - "encoding/base64" - "encoding/binary" - "encoding/hex" - "fmt" - "strings" - - "golang.org/x/crypto/bcrypt" -) - -const ( - alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" - alphanum = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" - idxbits = 6 - idxmask = 1<= 0; { - if remain == 0 { - cache, remain = CryptoRandInt(), idxmax - } - - if idx := int(cache & idxmask); idx < len(chars) { // nolint:gosec - sb.WriteByte(chars[idx]) - - i-- - } - - cache >>= idxbits - remain-- - } - - return sb.String() -} - -// CryptoRandInt function generates a random 64-bit unsigned integer using cryptographic methods -func CryptoRandInt() uint64 { - buf := make([]byte, ByteLength) - if _, err := rand.Read(buf); err != nil { - panic(fmt.Errorf("cannot generate random number: %w", err)) - } - - return binary.BigEndian.Uint64(buf) -} - -// HashFromBytes returns a SHA-256 checksum of the input -func HashFromBytes(value []byte) string { - sum := sha256.Sum256(value) - return fmt.Sprintf("%x", sum) -} - -// Hash returns a SHA-256 checksum of a string -func Hash(value string) string { - return HashFromBytes([]byte(value)) -} - -// GenerateRandomBytes returns random bytes -func GenerateRandomBytes(size int) []byte { - b := make([]byte, size) - if _, err := rand.Read(b); err != nil { - panic(err) - } - - return b -} - -// GenerateRandomString returns a random string -func GenerateRandomString(size int) string { - return base64.URLEncoding.EncodeToString(GenerateRandomBytes(size)) -} - -// GenerateRandomStringHex returns a random hexadecimal string -func GenerateRandomStringHex(size int) string { - return hex.EncodeToString(GenerateRandomBytes(size)) -} - -// HashInput function takes an input and generates a bcrypt hash -func HashInput(input string) (string, error) { - bytes, err := bcrypt.GenerateFromPassword([]byte(input), bcrypt.DefaultCost) - - return string(bytes), err -} - -// GenerateSHA256Hmac generates a SHA-256 HMAC by using the secret as the key and -// the data as the message -func GenerateSHA256Hmac(secret string, data []byte) string { - h := hmac.New(sha256.New, []byte(secret)) - h.Write(data) - - return hex.EncodeToString(h.Sum(nil)) -} diff --git a/pkg/keygen/keygen_test.go b/pkg/keygen/keygen_test.go deleted file mode 100644 index f3d0ede6..00000000 --- a/pkg/keygen/keygen_test.go +++ /dev/null @@ -1,136 +0,0 @@ -package keygen_test - -import ( - "math/rand" - "regexp" - "strings" - "testing" - "time" - - "github.com/stretchr/testify/require" - - "github.com/theopenlane/core/pkg/keygen" -) - -func TestAlpha(t *testing.T) { - // This is a long running test, skip if in short mode - if testing.Short() { - t.Skip("skipping long running test in short mode") - } - - // Test creating different random strings at different lengths - for i := 0; i < 10000; i++ { - len := rand.Intn(512) + 1 //nolint:gosec - alpha := keygen.Alpha(len) - require.Len(t, alpha, len) - require.Regexp(t, regexp.MustCompile(`[a-zA-Z]+`), alpha) - } - - vals := make(map[string]struct{}) - - for i := 0; i < 10000; i++ { - val := keygen.Alpha(16) - vals[val] = struct{}{} - } - - require.Len(t, vals, 10000, "there is a very low chance that a duplicate value was generated") -} - -func TestAlphaNumeric(t *testing.T) { - // This is a long running test, skip if in short mode - if testing.Short() { - t.Skip("skipping long running test in short mode") - } - - // Test creating different random strings at different lengths - for i := 0; i < 10000; i++ { - len := rand.Intn(512) + 1 //nolint:gosec - alpha := keygen.AlphaNumeric(len) - require.Len(t, alpha, len) - require.Regexp(t, regexp.MustCompile(`[a-zA-Z0-9]+`), alpha) - } - - vals := make(map[string]struct{}) - - for i := 0; i < 10000; i++ { - val := keygen.AlphaNumeric(16) - vals[val] = struct{}{} - } - - require.Len(t, vals, 10000, "there is a very low chance that a duplicate value was generated") -} - -func TestKeyGen(t *testing.T) { - // This is a long running test, skip if in short mode - if testing.Short() { - t.Skip("skipping long running test in short mode") - } - - keyID := keygen.KeyID() - require.Len(t, keyID, keygen.KeyIDLength) - - secret := keygen.Secret() - require.Len(t, secret, keygen.SecretLength) -} - -func TestCryptoRandInt(t *testing.T) { - // This is a long running test, skip if in short mode - if testing.Short() { - t.Skip("skipping long running test in short mode") - } - - nums := make(map[uint64]struct{}) - - for i := 0; i < 10000; i++ { - val := keygen.CryptoRandInt() - nums[val] = struct{}{} - } - - require.Len(t, nums, 10000, "there is a very low chance that a duplicate value was generated") -} - -func benchmarkAlpha(i int, b *testing.B) { - for n := 0; n < b.N; n++ { - keygen.Alpha(i) - } -} - -func benchmarkAlphaNumeric(i int, b *testing.B) { - for n := 0; n < b.N; n++ { - keygen.AlphaNumeric(i) - } -} - -func BenchmarkAlpha16(b *testing.B) { benchmarkAlpha(16, b) } -func BenchmarkAlpha64(b *testing.B) { benchmarkAlpha(64, b) } -func BenchmarkAlpha256(b *testing.B) { benchmarkAlpha(256, b) } - -func BenchmarkAlphaNumeric16(b *testing.B) { benchmarkAlphaNumeric(16, b) } -func BenchmarkAlphaNumeric64(b *testing.B) { benchmarkAlphaNumeric(64, b) } -func BenchmarkAlphaNumeric256(b *testing.B) { benchmarkAlphaNumeric(256, b) } - -func BenchmarkCryptoRandInt(b *testing.B) { - for n := 0; n < b.N; n++ { - keygen.CryptoRandInt() - } -} - -func BenchmarkRandInt(b *testing.B) { - source := rand.New(rand.NewSource(time.Now().UnixNano())) //nolint:gosec - for n := 0; n < b.N; n++ { - source.Int63() - } -} - -func TestPrefixedSecret(t *testing.T) { - // This is a long running test, skip if in short mode - if testing.Short() { - t.Skip("skipping long running test in short mode") - } - - prefix := "PREFIX" - secret := keygen.PrefixedSecret(prefix) - - require.True(t, strings.HasPrefix(secret, prefix), "secret should have the specified prefix") - require.Len(t, secret, len(prefix)+keygen.SecretLength+1, "secret should have the specified prefix and the length of a secret plus 1 for underscore") -} diff --git a/pkg/models/models.go b/pkg/models/models.go index 3e3538b6..68e6a801 100644 --- a/pkg/models/models.go +++ b/pkg/models/models.go @@ -8,7 +8,7 @@ import ( "github.com/theopenlane/utils/rout" "github.com/theopenlane/utils/ulids" - "github.com/theopenlane/core/pkg/passwd" + "github.com/theopenlane/utils/passwd" ) // ========= diff --git a/pkg/passwd/dk.go b/pkg/passwd/dk.go deleted file mode 100644 index 2ec68f76..00000000 --- a/pkg/passwd/dk.go +++ /dev/null @@ -1,127 +0,0 @@ -package passwd - -import ( - "bytes" - "crypto/rand" - "encoding/base64" - "fmt" - "regexp" - "strconv" - - "golang.org/x/crypto/argon2" -) - -// =========================================================================== -// Derived Key Algorithm -// =========================================================================== - -// Argon2 constants for the derived key (dk) algorithm -// See: https://cryptobook.nakov.com/mac-and-key-derivation/argon2 -const ( - dkAlg = "argon2id" // the derived key algorithm - dkTime = uint32(1) // draft RFC recommends time = 1 - dkMem = uint32(64 * 1024) // draft RFC recommends memory as ~64MB (or as much as possible) - dkProc = uint8(2) // can be set to the number of available CPUs - dkSLen = 16 // the length of the salt to generate per user - dkKLen = uint32(32) // the length of the derived key (32 bytes is the required key size for AES-256) -) - -// Argon2 variables for the derived key (dk) algorithm -var ( - dkParse = regexp.MustCompile(`^\$(?P[\w\d]+)\$v=(?P\d+)\$m=(?P\d+),t=(?P