Skip to content
Merged

mege #114

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ The Toolkit currently offers the following packages:
2. [**errors**](errors): Enhanced error handling utilities, such as error wrapping, context propagation, and error
inspection.
3. [**idgen**](idgen): A package for generating unique identifiers.
4. [**crypto**](crypto): A powerful library designed to provide cryptographic functionalities, including hashing, encryption, and decryption. This toolkit is built with performance and security in mind, making it an essential component for any application that requires secure data handling.

## Getting Started

Expand Down
96 changes: 96 additions & 0 deletions crypto/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# Crypto Toolkit

## Overview

The Crypto Toolkit is a powerful library designed to provide cryptographic functionalities, including hashing, encryption, and decryption. This toolkit is built with performance and security in mind, making it an essential component for any application that requires secure data handling.

## Hashing

One of the standout features of the Crypto Toolkit is its robust hashing capabilities. Hashing is a fundamental aspect of cryptography, used to ensure data integrity and authenticity. The toolkit supports various hashing algorithms, allowing developers to choose the most suitable one for their needs.

### Key Features of Hashing:

- **Speed and Efficiency**: The hashing functions are optimized for performance, ensuring that even large datasets can be processed quickly without compromising security.
- **Multiple Algorithms**: The toolkit supports a variety of hashing algorithms, including SHA-256, SHA-512, Argon2, Bcrypt, Scrypt, and others, providing flexibility for different use cases. This allows for easy upgrades to stronger algorithms without modifying the password verification logic.
- **Algorithm Compatibility**: When users need to enhance security or switch algorithms, the toolkit ensures compatibility with existing hashed passwords. This means you can seamlessly transition to a new hashing algorithm without needing to rehash all stored passwords.
- **Built-in Salt Management**: The toolkit includes built-in salt management, eliminating the need for users to manage salts manually. This enhances security by ensuring that each hash is unique, even for identical inputs.
- **Collision Resistance**: The hashing algorithms are designed to minimize the risk of collisions, ensuring that each unique input produces a unique hash output.
- **Secure Data Integrity**: By using cryptographic hashes, you can verify the integrity of your data, ensuring that it has not been altered or tampered with during transmission or storage.
- **Customizable Parameters**: Each hashing algorithm allows for customizable parameters such as salt length, iteration count, and memory cost, enabling developers to fine-tune the security and performance of their hashing operations.
- **Caching Mechanism**: The toolkit includes a caching mechanism to optimize the verification process, reducing the computational overhead for frequently verified hashes.

### Supported Algorithms

The Crypto Toolkit supports a wide range of hashing algorithms, including but not limited to:

- **Argon2**: A modern, memory-hard hashing algorithm designed to resist GPU and ASIC attacks.
- **Bcrypt**: A widely-used hashing algorithm that is resistant to brute-force attacks.
- **Scrypt**: A memory-intensive hashing algorithm that is designed to be computationally expensive.
- **SHA-256/SHA-512**: Secure Hash Algorithms that are widely used for data integrity checks.
- **PBKDF2**: A key derivation function that is commonly used for password hashing.
- **HMAC**: A keyed-hash message authentication code that provides both data integrity and authenticity.

### Compatibility and Salt Management

The Crypto Toolkit is designed to be highly compatible with existing hashed passwords. When upgrading to a stronger hashing algorithm, the toolkit can still verify passwords that were hashed with the older algorithm. This ensures a smooth transition without requiring users to reset their passwords.
Additionally, the toolkit provides built-in salt management, which automatically generates and manages salts for each hash. This eliminates the need for developers to manually handle salts, reducing the risk of security vulnerabilities. However, the toolkit also supports custom salt generation, allowing developers to use their own salt values if needed.

### Example of Compatibility and Salt Management

```go
package main

import (
"fmt"

"github.com/origadmin/toolkits/crypto/hash"
"github.com/origadmin/toolkits/crypto/hash/types"
)

func main() {
// The hash is generated using the SHA-256 algorithm
sha256Crypto, err := hash.NewCrypto(types.TypeSha256)
if err != nil {
fmt.Println("Failed to create SHA-256 crypto:", err)
return
}

sha256Hash, err := sha256Crypto.Hash("myPassword123")
if err != nil {
fmt.Println("Failed to hash with SHA-256:", err)
return
}
fmt.Println("SHA-256 Hash:", sha256Hash)

// Upgrade to the Argon2 algorithm
argon2Crypto, err := hash.NewCrypto(types.TypeArgon2)
if err != nil {
fmt.Println("Failed to create Argon2 crypto:", err)
return
}

// Verify the old SHA-256 hash
err = argon2Crypto.Verify(sha256Hash, "myPassword123")
if err != nil {
fmt.Println("SHA-256 verification failed:", err)
} else {
fmt.Println("SHA-256 verification succeeded")
}

// Use Argon2 to generate a new hash
argon2Hash, err := argon2Crypto.Hash("myPassword123")
if err != nil {
fmt.Println("Failed to hash with Argon2:", err)
return
}
fmt.Println("Argon2 Hash:", argon2Hash)

// Verify the Argon2 hash
err = argon2Crypto.Verify(argon2Hash, "myPassword123")
if err != nil {
fmt.Println("Argon2 verification failed:", err)
} else {
fmt.Println("Argon2 verification succeeded")
}
}
```
69 changes: 47 additions & 22 deletions crypto/hash/examples/hash/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,49 +2,74 @@
* Copyright (c) 2024 OrigAdmin. All rights reserved.
*/

// Package main provides an example of using the hash package
package main

import (
"fmt"
"log"

"github.com/origadmin/toolkits/crypto/hash"
"github.com/origadmin/toolkits/crypto/hash/algorithms/argon2"
"github.com/origadmin/toolkits/crypto/hash/types"
)

func main() {
// The hash is generated using the SHA-256 algorithm
sha256Crypto, err := hash.NewCrypto(types.TypeSha256)
if err != nil {
fmt.Println("Failed to create SHA-256 crypto:", err)
return
}

sha256Hash, err := sha256Crypto.Hash("myPassword123")
if err != nil {
fmt.Println("Failed to hash with SHA-256:", err)
return
}
fmt.Println("SHA-256 Hash:", sha256Hash)

// reate cryptographic instance
crypto, err := hash.NewCrypto(types.TypeArgon2, func(config *types.Config) {
config.SaltLength = 16
config.ParamConfig = argon2.DefaultParams().String()
})
// Verify the SHA-256 hash
err = sha256Crypto.Verify(sha256Hash, "myPassword123")
if err != nil {
log.Fatal(err)
fmt.Println("SHA-256 verification failed:", err)
} else {
fmt.Println("SHA-256 verification succeeded")
}

// Test password
password := "test123"
// Upgrade to the Argon2 algorithm
argon2Crypto, err := hash.NewCrypto(types.TypeArgon2)
if err != nil {
fmt.Println("Failed to create Argon2 crypto:", err)
return
}

// Use Argon2 to generate a new hash
argon2Hash, err := argon2Crypto.Hash("myPassword123")
if err != nil {
fmt.Println("Failed to hash with Argon2:", err)
return
}
fmt.Println("Argon2 Hash:", argon2Hash)

// Generate hash
hashed, err := crypto.Hash(password)
// Verify the Argon2 hash
err = argon2Crypto.Verify(argon2Hash, "myPassword123")
if err != nil {
log.Fatal(err)
fmt.Println("Argon2 verification failed:", err)
} else {
fmt.Println("Argon2 verification succeeded")
}
fmt.Printf("Generated hash: %s\n", hashed)

// Verify password
err = crypto.Verify(hashed, password)
// Verify the old hash
err = argon2Crypto.Verify(sha256Hash, "myPassword123")
if err != nil {
log.Fatal(err)
fmt.Println("Argon2 SHA-256 verification failed:", err)
} else {
fmt.Println("Argon2 SHA-256 verification succeeded")
}
fmt.Println("Password verified successfully!")

// Test wrong password
wrongPassword := "wrong123"
err = crypto.Verify(hashed, wrongPassword)
err = hash.Verify(sha256Hash, "myPassword123")
if err != nil {
fmt.Println("Wrong password detected as expected")
fmt.Println("Global SHA-256 verification failed:", err)
} else {
fmt.Println("Global SHA-256 verification succeeded")
}
}
7 changes: 5 additions & 2 deletions idgen/go.mod → identifier/go.mod
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
module github.com/origadmin/toolkits/idgen
module github.com/origadmin/toolkits/identifier

go 1.22.5
go 1.23.1

toolchain go1.23.7

require (
github.com/bwmarrin/snowflake v0.3.0
github.com/goexts/generic v0.2.4
github.com/google/uuid v1.6.0
github.com/oklog/ulid/v2 v2.1.0
github.com/rs/xid v1.6.0
Expand Down
2 changes: 2 additions & 0 deletions idgen/go.sum → identifier/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ github.com/bwmarrin/snowflake v0.3.0 h1:xm67bEhkKh6ij1790JB83OujPR5CzNe8QuQqAgIS
github.com/bwmarrin/snowflake v0.3.0/go.mod h1:NdZxfVWX+oR6y2K0o6qAYv6gIOP9rjG0/E9WsDpxqwE=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/goexts/generic v0.2.4 h1:RDcE/GtudVUxWb+hSP9l1jFirkWEkZHrnHfiY4WWFG4=
github.com/goexts/generic v0.2.4/go.mod h1:j/ZjWHYt+If6VjeHWvDhYKdoP+gAiAKpm0cu3yvKmfo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/oklog/ulid/v2 v2.1.0 h1:+9lhoxAP56we25tyYETBBY1YLA2SaoLvUFgrP2miPJU=
Expand Down
68 changes: 68 additions & 0 deletions identifier/identifier.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Copyright (c) 2024 OrigAdmin. All rights reserved.
*/

// Package identifier provides a unified interface for generating and validating unique identifiers.
package identifier

// Identifier defines the basic interface for all identifier types.
type Identifier interface {
Name() string // Name returns the name of the identifier.
Size() int // Size returns the size of the identifier in bits.
}

// StringValidator defines the interface for validating string identifiers.
type StringValidator interface {
ValidateString(string) bool // ValidateString checks if the provided string is a valid identifier.
}

// StringGenerator defines the interface for generating string identifiers.
type StringGenerator interface {
GenerateString() string // GenerateString generates a new string identifier.
}

// NumberValidator defines the interface for validating number identifiers.
type NumberValidator interface {
ValidateNumber(int64) bool // ValidateNumber checks if the provided number is a valid identifier.
}

// NumberGenerator defines the interface for generating number identifiers.
type NumberGenerator interface {
GenerateNumber() int64 // GenerateNumber generates a new number identifier.
}

// StringIdentifier combines the Identifier, StringValidator, and StringGenerator interfaces.
type StringIdentifier interface {
Identifier
StringValidator
StringGenerator
}

// NumberIdentifier combines the Identifier, NumberValidator, and NumberGenerator interfaces.
type NumberIdentifier interface {
Identifier
NumberValidator
NumberGenerator
}

// MultiTypeIdentifier combines the StringIdentifier and NumberIdentifier interfaces.
type MultiTypeIdentifier interface {
StringIdentifier
NumberIdentifier
}

type TypedIdentifier[T ~int64 | ~string] interface {
Identifier
Generate() T
Validate(T) bool
}

// SetDefaultIdentifier sets the default identifier generator.
func SetDefaultIdentifier(gen Identifier) {
switch v := gen.(type) {
case StringIdentifier:
registry.SetDefaultString(v)
case NumberIdentifier:
registry.SetDefaultNumber(v)
}
}
42 changes: 27 additions & 15 deletions idgen/ksuid/ksuid.go → identifier/ksuid/ksuid.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,51 +7,61 @@ package shortid
import (
"github.com/segmentio/ksuid"

"github.com/origadmin/toolkits/idgen"
"github.com/origadmin/toolkits/identifier"
)

var (
bitSize = 27 // bitSize is used to store the length of generated ID.
bitSize = len(ksuid.New().String()) // bitSize is used to store the length of generated ID.
)

// init registers the Snowflake generator with the ident package and initializes bitSize.
func init() {
idgen.RegisterStringIdentifier(New())
s := New()
bitSize = len(s.GenerateString())
identifier.RegisterStringIdentifier(s)
}

type KSUID struct {
generator ksuid.KSUID
}

func (s KSUID) ValidateString(id string) bool {
if len(id) != bitSize {
return false
}
_, err := ksuid.Parse(id)
return err == nil
}

func (s KSUID) GenerateString() string {
return s.generator.String()
}

// Name returns the name of the generator.
func (s KSUID) Name() string {
return "ksuid"
}

// Gen generates a new KSUID ID as a string.
func (s KSUID) String() string {
return s.generator.String()
// Generate generates a new KSUID ID as a string.
func (s KSUID) Generate() string {
return s.GenerateString()
}

// ValidateString checks if the provided ID is a valid KSUID ID.
func (s KSUID) ValidateString(id string) bool {
if len(id) != bitSize {
return false
}
_, err := ksuid.Parse(id)
return err == nil
// Validate checks if the provided ID is a valid KSUID ID.
func (s KSUID) Validate(id string) bool {
return s.ValidateString(id)
}

// Size returns the bit size of the generated KSUID ID.
func (s KSUID) Size() int {
return bitSize
}

type Setting struct {
type Options struct {
}

// New creates a new KSUID generator with a unique node.
func New(_ ...Setting) *KSUID {
func New(_ ...Options) *KSUID {
generator, err := ksuid.NewRandom()
if err != nil {
panic(err)
Expand All @@ -60,3 +70,5 @@ func New(_ ...Setting) *KSUID {
generator: generator,
}
}

var _ identifier.TypedIdentifier[string] = &KSUID{}
Loading