This repository has been archived on 2024-02-27. You can view files and clone it, but cannot push or open issues or pull requests.
gootp/internal/andotp/andotp.go

96 lines
2.3 KiB
Go

package andotp
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/sha1"
"encoding/binary"
"fmt"
"math/big"
"golang.org/x/crypto/pbkdf2"
)
const (
ivLen = 12
keyLen = 32
iterationLen = 4
saltLen = 12
maxIterations = 160000
minIterations = 140000
)
func Encrypt(password, plaintext []byte) ([]byte, error) {
iter := make([]byte, iterationLen)
iv := make([]byte, ivLen)
salt := make([]byte, saltLen)
maxMinIterationsSubtracted, err := rand.Int(rand.Reader, big.NewInt(int64(maxIterations-minIterations)))
if err != nil {
return nil, fmt.Errorf("rand.Int: %w", err)
}
iterations := int(maxMinIterationsSubtracted.Int64() + minIterations)
binary.BigEndian.PutUint32(iter, uint32(iterations))
_, err = rand.Read(iv)
if err != nil {
return nil, fmt.Errorf("rand.Read: %w", err)
}
_, err = rand.Read(salt)
if err != nil {
return nil, fmt.Errorf("rand.Read: %w", err)
}
secretKey := pbkdf2.Key(password, salt, iterations, keyLen, sha1.New)
block, err := aes.NewCipher(secretKey)
if err != nil {
return nil, fmt.Errorf("aes.NewCipher: %w", err)
}
aesGCM, err := cipher.NewGCM(block)
if err != nil {
return nil, fmt.Errorf("cipher.NewGCM: %w", err)
}
cipherText := aesGCM.Seal(nil, iv, plaintext, nil)
finalCipher := make([]byte, 0, len(iter)+len(salt)+len(iv)+len(cipherText))
finalCipher = append(finalCipher, iter...)
finalCipher = append(finalCipher, salt...)
finalCipher = append(finalCipher, iv...)
finalCipher = append(finalCipher, cipherText...)
return finalCipher, nil
}
func Decrypt(password, text []byte) ([]byte, error) {
iterations := text[:iterationLen]
salt := text[iterationLen : iterationLen+saltLen]
iv := text[iterationLen+saltLen : iterationLen+saltLen+ivLen]
cipherText := text[iterationLen+saltLen+ivLen:]
iter := int(binary.BigEndian.Uint32(iterations))
secretKey := pbkdf2.Key(password, salt, iter, keyLen, sha1.New)
block, err := aes.NewCipher(secretKey)
if err != nil {
return nil, fmt.Errorf("aes.NewCipher: %w", err)
}
aesGCM, err := cipher.NewGCM(block)
if err != nil {
return nil, fmt.Errorf("cipher.NewGCM: %w", err)
}
plaintext, err := aesGCM.Open(nil, iv, cipherText, nil)
if err != nil {
return nil, fmt.Errorf("aesGCM.Open: %w", err)
}
return plaintext, nil
}