2020-03-23 06:06:44 +00:00
|
|
|
package crypto
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/rand"
|
|
|
|
"time"
|
|
|
|
|
2023-12-08 14:30:55 +00:00
|
|
|
"github.com/zitadel/zitadel/internal/zerrors"
|
2020-03-23 06:06:44 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
2020-04-21 15:00:32 +00:00
|
|
|
lowerLetters = []rune("abcdefghijklmnopqrstuvwxyz")
|
|
|
|
upperLetters = []rune("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
|
|
|
digits = []rune("0123456789")
|
|
|
|
symbols = []rune("~!@#$^&*()_+`-={}|[]:<>?,./")
|
2020-03-23 06:06:44 +00:00
|
|
|
)
|
|
|
|
|
2020-04-21 15:00:32 +00:00
|
|
|
type GeneratorConfig struct {
|
|
|
|
Length uint
|
2022-02-14 16:22:30 +00:00
|
|
|
Expiry time.Duration
|
2020-04-21 15:00:32 +00:00
|
|
|
IncludeLowerLetters bool
|
|
|
|
IncludeUpperLetters bool
|
|
|
|
IncludeDigits bool
|
|
|
|
IncludeSymbols bool
|
|
|
|
}
|
|
|
|
|
2020-03-23 06:06:44 +00:00
|
|
|
type Generator interface {
|
|
|
|
Length() uint
|
|
|
|
Expiry() time.Duration
|
2024-04-05 09:35:49 +00:00
|
|
|
Alg() EncryptionAlgorithm
|
2020-03-23 06:06:44 +00:00
|
|
|
Runes() []rune
|
|
|
|
}
|
|
|
|
|
2020-04-21 15:00:32 +00:00
|
|
|
type generator struct {
|
2020-03-23 06:06:44 +00:00
|
|
|
length uint
|
|
|
|
expiry time.Duration
|
|
|
|
runes []rune
|
|
|
|
}
|
|
|
|
|
2020-04-21 15:00:32 +00:00
|
|
|
func (g *generator) Length() uint {
|
2020-03-23 06:06:44 +00:00
|
|
|
return g.length
|
|
|
|
}
|
|
|
|
|
2020-04-21 15:00:32 +00:00
|
|
|
func (g *generator) Expiry() time.Duration {
|
2020-03-23 06:06:44 +00:00
|
|
|
return g.expiry
|
|
|
|
}
|
|
|
|
|
2020-04-21 15:00:32 +00:00
|
|
|
func (g *generator) Runes() []rune {
|
2020-03-23 06:06:44 +00:00
|
|
|
return g.runes
|
|
|
|
}
|
|
|
|
|
2020-04-21 15:00:32 +00:00
|
|
|
type encryptionGenerator struct {
|
|
|
|
generator
|
|
|
|
alg EncryptionAlgorithm
|
2020-03-23 06:06:44 +00:00
|
|
|
}
|
|
|
|
|
2024-04-05 09:35:49 +00:00
|
|
|
func (g *encryptionGenerator) Alg() EncryptionAlgorithm {
|
2020-04-21 15:00:32 +00:00
|
|
|
return g.alg
|
2020-03-23 06:06:44 +00:00
|
|
|
}
|
|
|
|
|
2020-04-21 15:00:32 +00:00
|
|
|
func NewEncryptionGenerator(config GeneratorConfig, algorithm EncryptionAlgorithm) Generator {
|
|
|
|
return &encryptionGenerator{
|
|
|
|
newGenerator(config),
|
|
|
|
algorithm,
|
|
|
|
}
|
2020-03-23 06:06:44 +00:00
|
|
|
}
|
|
|
|
|
2024-04-05 09:35:49 +00:00
|
|
|
type HashGenerator struct {
|
2020-04-21 15:00:32 +00:00
|
|
|
generator
|
2024-04-05 09:35:49 +00:00
|
|
|
hasher *Hasher
|
2020-03-23 06:06:44 +00:00
|
|
|
}
|
|
|
|
|
2024-04-05 09:35:49 +00:00
|
|
|
func NewHashGenerator(config GeneratorConfig, hasher *Hasher) *HashGenerator {
|
|
|
|
return &HashGenerator{
|
|
|
|
newGenerator(config),
|
|
|
|
hasher,
|
|
|
|
}
|
2020-03-23 06:06:44 +00:00
|
|
|
}
|
|
|
|
|
2024-04-05 09:35:49 +00:00
|
|
|
func (g *HashGenerator) NewCode() (encoded, plain string, err error) {
|
|
|
|
plain, err = GenerateRandomString(g.Length(), g.Runes())
|
|
|
|
if err != nil {
|
|
|
|
return "", "", err
|
|
|
|
}
|
|
|
|
encoded, err = g.hasher.Hash(plain)
|
|
|
|
if err != nil {
|
|
|
|
return "", "", err
|
2020-04-21 15:00:32 +00:00
|
|
|
}
|
2024-04-05 09:35:49 +00:00
|
|
|
return encoded, plain, nil
|
2020-03-23 06:06:44 +00:00
|
|
|
}
|
|
|
|
|
2020-04-21 15:00:32 +00:00
|
|
|
func newGenerator(config GeneratorConfig) generator {
|
|
|
|
var runes []rune
|
|
|
|
if config.IncludeLowerLetters {
|
|
|
|
runes = append(runes, lowerLetters...)
|
|
|
|
}
|
|
|
|
if config.IncludeUpperLetters {
|
|
|
|
runes = append(runes, upperLetters...)
|
|
|
|
}
|
|
|
|
if config.IncludeDigits {
|
|
|
|
runes = append(runes, digits...)
|
|
|
|
}
|
|
|
|
if config.IncludeSymbols {
|
|
|
|
runes = append(runes, symbols...)
|
|
|
|
}
|
|
|
|
return generator{
|
|
|
|
length: config.Length,
|
2022-02-14 16:22:30 +00:00
|
|
|
expiry: config.Expiry,
|
2020-03-23 06:06:44 +00:00
|
|
|
runes: runes,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewCode(g Generator) (*CryptoValue, string, error) {
|
2022-05-16 09:26:24 +00:00
|
|
|
code, err := GenerateRandomString(g.Length(), g.Runes())
|
2020-03-23 06:06:44 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, "", err
|
|
|
|
}
|
|
|
|
crypto, err := Crypt([]byte(code), g.Alg())
|
|
|
|
if err != nil {
|
|
|
|
return nil, "", err
|
|
|
|
}
|
|
|
|
return crypto, code, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func IsCodeExpired(creationDate time.Time, expiry time.Duration) bool {
|
2020-04-21 15:00:32 +00:00
|
|
|
if expiry == 0 {
|
|
|
|
return false
|
|
|
|
}
|
2020-03-23 06:06:44 +00:00
|
|
|
return creationDate.Add(expiry).Before(time.Now().UTC())
|
|
|
|
}
|
|
|
|
|
2024-04-05 09:35:49 +00:00
|
|
|
func VerifyCode(creationDate time.Time, expiry time.Duration, cryptoCode *CryptoValue, verificationCode string, algorithm EncryptionAlgorithm) error {
|
2020-03-23 06:06:44 +00:00
|
|
|
if IsCodeExpired(creationDate, expiry) {
|
2023-12-08 14:30:55 +00:00
|
|
|
return zerrors.ThrowPreconditionFailed(nil, "CODE-QvUQ4P", "Errors.User.Code.Expired")
|
2020-03-23 06:06:44 +00:00
|
|
|
}
|
2024-04-05 09:35:49 +00:00
|
|
|
return verifyEncryptedCode(cryptoCode, verificationCode, algorithm)
|
2020-03-23 06:06:44 +00:00
|
|
|
}
|
|
|
|
|
2022-05-16 09:26:24 +00:00
|
|
|
func GenerateRandomString(length uint, chars []rune) (string, error) {
|
2020-03-23 06:06:44 +00:00
|
|
|
if length == 0 {
|
|
|
|
return "", nil
|
|
|
|
}
|
|
|
|
|
|
|
|
max := len(chars) - 1
|
|
|
|
maxStr := int(length - 1)
|
|
|
|
|
|
|
|
str := make([]rune, length)
|
|
|
|
randBytes := make([]byte, length)
|
|
|
|
if _, err := rand.Read(randBytes); err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
for i, rb := range randBytes {
|
|
|
|
str[i] = chars[int(rb)%max]
|
|
|
|
if i == maxStr {
|
|
|
|
return string(str), nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return "", nil
|
|
|
|
}
|
|
|
|
|
2020-03-30 07:28:00 +00:00
|
|
|
func verifyEncryptedCode(cryptoCode *CryptoValue, verificationCode string, alg EncryptionAlgorithm) error {
|
2020-03-30 09:26:02 +00:00
|
|
|
if cryptoCode == nil {
|
2023-12-08 14:30:55 +00:00
|
|
|
return zerrors.ThrowInvalidArgument(nil, "CRYPT-aqrFV", "Errors.User.Code.CryptoCodeNil")
|
2020-03-30 09:26:02 +00:00
|
|
|
}
|
2020-03-23 06:06:44 +00:00
|
|
|
code, err := DecryptString(cryptoCode, alg)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if code != verificationCode {
|
2023-12-08 14:30:55 +00:00
|
|
|
return zerrors.ThrowInvalidArgument(nil, "CODE-woT0xc", "Errors.User.Code.Invalid")
|
2020-03-23 06:06:44 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|