Skip to content

Commit

Permalink
feat(chacha20poly1305): working functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
aldy505 committed May 28, 2021
1 parent 8bf58a1 commit 10068d1
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 15 deletions.
52 changes: 46 additions & 6 deletions chacha20poly1305/chacha20poly1305.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ package chacha20poly1305
import (
"crypto/rand"
"encoding/hex"
"fmt"
"errors"
"io"
"strings"

"github.com/aldy505/phc-crypto/format"
"golang.org/x/crypto/chacha20poly1305"
Expand All @@ -21,17 +22,56 @@ func Hash(plain string) (string, error) {

// Select a random nonce, and leave capacity for the ciphertext.
nonce := make([]byte, aead.NonceSize(), aead.NonceSize()+len(plain)+aead.Overhead())
fmt.Println(nonce)
io.ReadFull(rand.Reader, nonce)
// Encrypt the message and append the ciphertext to the nonce.
hash := aead.Seal(nonce, nonce, []byte(plain), nil)

hashString := format.Serialize(format.PHCConfig{
Hash: hex.EncodeToString(hash),
Salt: hex.EncodeToString(salt),
Params: map[string]interface{}{
"n": hex.EncodeToString(nonce),
},
ID: "chacha20poly1305",
ID: "chacha20poly1305",
})
return hashString, nil
}

func Decrypt(salt []byte, hash []byte) (string, error) {
aead, err := chacha20poly1305.NewX(salt)
if err != nil {
return "", err
}

nonce, cipherText := hash[:aead.NonceSize()], hash[aead.NonceSize():]

decrypted, err := aead.Open(nil, nonce, cipherText, nil)
if err != nil {
return "", err
}
return string(decrypted), nil
}

func Verify(hash string, plain string) (bool, error) {
deserialize := format.Deserialize(hash)
if !strings.HasPrefix(deserialize.ID, "chacha20poly1305") {
return false, errors.New("hashed string is not a chacha20poly1305 instance")
}

salt, err := hex.DecodeString(deserialize.Salt)
if err != nil {
return false, err
}

decodedHash, err := hex.DecodeString(deserialize.Hash)
if err != nil {
return false, err
}

decrypted, err := Decrypt(salt, decodedHash)
if err != nil {
return false, err
}

if decrypted == plain {
return true, nil
}
return false, nil
}
19 changes: 14 additions & 5 deletions format/format.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,13 @@ type PHCConfig struct {
func Serialize(config PHCConfig) string {
var params []string
for key, value := range config.Params {
params = append(params, key+"="+strconv.Itoa(value.(int)))
switch v := value.(type) {
case string:
params = append(params, key+"="+v)
case int:
params = append(params, key+"="+strconv.Itoa(v))
}

}
return "$" + config.ID + "$v=" + strconv.Itoa(config.Version) + "$" + strings.Join(params, ",") + "$" + config.Salt + "$" + config.Hash
}
Expand All @@ -29,11 +35,14 @@ func Deserialize(hash string) PHCConfig {
hashArray := strings.Split(hash, "$")
params := make(map[string]interface{})

paramsArray := strings.Split(hashArray[3], ",")
for _, value := range paramsArray {
pair := strings.Split(value, "=")
params[pair[0]] = pair[1]
if len(hashArray[3]) != 0 {
paramsArray := strings.Split(hashArray[3], ",")
for _, value := range paramsArray {
pair := strings.Split(value, "=")
params[pair[0]] = pair[1]
}
}

version, _ := strconv.Atoi(hashArray[2])
return PHCConfig{
ID: hashArray[1],
Expand Down
12 changes: 8 additions & 4 deletions phc-crypto.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func Use(name string, config Config) (*Algo, error) {
var algo *Algo

algos := []string{"scrypt", "pbkdf2", "chacha20poly1305", "bcrypt", "argon2"}
check := in_array(name, algos)
check := inArray(name, algos)

if check {
algo = &Algo{
Expand All @@ -44,7 +44,8 @@ func Use(name string, config Config) (*Algo, error) {
}
}

// Hash returns a PHC formatted string of a hash function (that was initiated from Use)
// Hash returns a PHC formatted string of a hash function (that was initiated from Use).
// Example usage:
func (a *Algo) Hash(plain string) (hash string, err error) {
if a.Name == "scrypt" {
hash, err = scrypt.Hash(plain, scrypt.Config{
Expand Down Expand Up @@ -84,7 +85,7 @@ func (a *Algo) Hash(plain string) (hash string, err error) {
return
}

// Hash returns a boolean of a hash function (that was initiated from Use)
// Verify returns a boolean of a hash function (that was initiated from Use).
func (a *Algo) Verify(hash, plain string) (verify bool, err error) {
if a.Name == "scrypt" {
verify, err = scrypt.Verify(hash, plain)
Expand All @@ -98,13 +99,16 @@ func (a *Algo) Verify(hash, plain string) (verify bool, err error) {
} else if a.Name == "pbkdf2" {
verify, err = pbkdf2.Verify(hash, plain)
return
} else if a.Name == "chacha20poly1305" {
verify, err = chacha20poly1305.Verify(hash, plain)
return
}
verify = false
err = errors.New("the algorithm provided is not (yet) supported")
return
}

func in_array(val string, array []string) bool {
func inArray(val string, array []string) bool {
for i := range array {
if ok := array[i] == val; ok {
return true
Expand Down
58 changes: 58 additions & 0 deletions phc-crypto_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -260,4 +260,62 @@ func TestPHCCrypto(t *testing.T) {
}
})
})
t.Run("chacha20poly1305 test", func(t *testing.T) {
t.Run("should be ok without additional config", func(t *testing.T) {
crypto, err := phccrypto.Use("chacha20poly1305", phccrypto.Config{})
if err != nil {
t.Error(err)
}
hash, err := crypto.Hash("password123")
if err != nil {
t.Error(err)
}
typeof := reflect.TypeOf(hash).Kind()
if typeof != reflect.String {
t.Error("returned type is not string")
}
})
t.Run("verify should return true", func(t *testing.T) {
crypto, err := phccrypto.Use("chacha20poly1305", phccrypto.Config{})
if err != nil {
t.Error(err)
}
hash, err := crypto.Hash("password123")
if err != nil {
t.Error(err)
}
verify, err := crypto.Verify(hash, "password123")
if err != nil {
t.Error(err)
}
typeof := reflect.TypeOf(verify).Kind()
if typeof != reflect.Bool {
t.Error("returned type is not boolean")
}
if !verify {
t.Error("verify function returned false")
}
})
t.Run("verify should return false", func(t *testing.T) {
crypto, err := phccrypto.Use("chacha20poly1305", phccrypto.Config{})
if err != nil {
t.Error(err)
}
hash, err := crypto.Hash("password123")
if err != nil {
t.Error(err)
}
verify, err := crypto.Verify(hash, "password321")
if err != nil {
t.Error(err)
}
typeof := reflect.TypeOf(verify).Kind()
if typeof != reflect.Bool {
t.Error("returned type is not boolean")
}
if verify {
t.Error("verify function returned false")
}
})
})
}

0 comments on commit 10068d1

Please sign in to comment.