goeznacl

package module
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 10, 2022 License: MIT Imports: 23 Imported by: 5

README

goeznacl

goeznacl is an MIT-licensed Go library for making work with cryptography easier by providing an easy-to-use API for public key encryption, secret key encryption, digital signatures, data hashing, and password hashing. It was originally written strictly as a wrapper around the NaCl implementation provided in the Go main libraries, but it has since added support for other algorithms beyond those provided by NaCl and retains the 'goeznacl' name for kicks and giggles.

Description

Cryptography is really hard. Any code which implements it is equally hard. Anything which touches the implementation code isn't much easier. This library came from a need to work with crypto keys over a text-based protocol, but as it matured, I discovered it also made debugging code which interacts with cryptography much easier, too. The library as a whole should be considered beta, but is progressing toward maturity fairly quickly.

Please don't use this code to place important crypto keys in your code or embed backdoors. No one needs that kind of drama.

Usage

The code itself is well-commented, and usage should be pretty obvious, but if you want a good 10,000 foot view, read on. A full reference of the library can be found here.

CryptoString

For starters, a new data type, CryptoString, is used heavily when interacting with this library. In short, CryptoStrings are Base85-encoded binary data -- usually hashes or crypto keys -- that have an algorithm name prepended and a colon separating the two. Nothing earth-shattering, but it makes a genuine difference. It's a means to a goal, so here's how you use it:

  • To make one from existing data, call NewCSFromBytes().
  • NewCS() is for making one from a string containing CryptoString data
  • AsString() returns prefix and encoded data as a string. AsBytes() returns the same thing as a byte slice.
  • AsRaw() returns the raw, unencoded binary data and no prefix
Encryption, Decryption, and Digital Signatures

Even with goeznacl, encryption isn't just a matter of waving the magic Encryption Wand, but it's pretty close. Go look at examples_test.go for some examples of the process using goeznacl with lots of detailed comments for help.

Hashing

A cryptographic hash is a string of bytes which, in theory, uniquely represent a chunk of data. Hashes can be used to identify data without giving the data itself away, so a hash can be used to identify a secret key without compromising the key itself, for example. The hashing capabilities built into goeznacl operate from the assumption that what you're hashing will fit into RAM. If this isn't the case, you'll want to interact with the hashing libraries directly and you can use the sources for GetHash() for some direction on how to do this.

NOTE: If you are looking to hash passwords, DO NOT use the regular hashing facilities. Instead, jump down to the next section on password hashing.

import (
	"fmt"
	ezn "gitlab.com/darkwyrm/goeznacl"
)

func main() {
	mySecretKey := ezn.GenerateSecretKey(ezn.PreferredSecretType())
	keyHash, err := ezn.GetHash(ezn.PreferredHashType(), mySecretKey.Key.AsBytes())
	if err != nil {
		panic(err)
	}

	fmt.Printf("My Secret Key:\n%s\n", mySecretKey.Key.AsString())
	fmt.Printf("Hash of my secret key:\n%s\n", keyHash.AsString())
}

The output of this little example looks something like this:

My Secret Key:
AES-256:VAv|e5|}hdcWs)<zF32m%Yj8OMACApmv_yKeV=wl
Hash of my secret key:
BLAKE3-256:5M{w<@~<RG`qt_J10+Aw1+Mtg#Os9*p|o$6(b+a1

For those more familiar with working with hashing algorithms, you might be a bit suprised the BLAKE3-256 prefix. The original BLAKE algorithm was a runner up to what became the SHA3 algorithm and BLAKE3 is a couple iterations later on the original. BLAKE3 many times faster than SHA256 and provides 128 bits of security, which is enough for most needs. Other algorithms, such as SHAKE and SHA2 are available. GetHashAlgorithms() will return a list of all available hashing algorithms supported by the library, including SHA-256 and SHA-512.

Password Hashing

Unlike regular hashing, which is designed to be fast, password hashing is similar except that the hashing algorithm is designed to be resource intensive to prevent bad actors from breaking a hash by throwing a GPU cluster at it. Although some algorithms out there, such as scrypt and bcrypt, are often used for hashing passwords, these methods just run regular hash algorithms ten or hundreds of thousands of times to make things harder. goeznacl uses another: Argon2id, an algorithm that came out of the Password Hashing Competition from the 2010s.

Hashing a password with goeznacl is very straightforward: HashPassword() takes a string containing a password creates an Argon2id hash, and VerifyPasswordHash() takes a string containing a password and another string containing a password hash and returning true if they match. Argon2id has a lot of options for tuning performance; HashPassword() chooses a set of values which should work well for most user-facing situations. If this is not acceptable, use the source for HashPassword() for some guidance on how to use the argon2 module directly.

Other Algorithms and Certified Algorithm Support

Some use cases will require support for specific or multiple algorithms. For these needs are the functions GetAsymmetricAlgorithms(), GetSecretAlgorithms(), GetSigningAlgorithms(), and GetHashAlgorithms(), which return a slice of strings containing the names of all algorithms supported. There are corresponding calls PreferredAsymmetricType(), PreferredSecretType(), PreferredSigningType(), and PreferredHashType() for instances where the library chooses for you if you're not sure which one is best.

For situations where certified algorithms are required for compliance, RequireCertifiedAlgorithms(true) will exclude algorithms which are not certified. This library uses the U.S. government's Federal Information Processing Standards (FIPS) for choosing algorithms. This is not a guarantee of any kind of compliance, only an attempt to assist, as compliance almost always requires careful application of compliant algorithms and more hoops to jump through than the Barnum and Bailey Circus. Confirming compliance in your area will require consulting official government resources.

As of this writing, goeznacl supports the following algorithms:

  • Symmetric (Secret Key) Encryption
    • AES-128
    • AES-256*
    • XSALSA20
  • Asymmetric (Public Key) Encryption
    • CURVE25519*
    • RSA2048-SHA256, 2048-bit RSA which uses SHA256 for internal hash calculations
  • Digital Signatures
    • ECDSA-P256-SHA256, ECDSA which uses the P-256 elliptic curve and SHA256 for internal hash calculations
    • ED25519*
  • Cryptographic Hashes
    • BLAKE2B-256
    • BLAKE2B-512
    • BLAKE3-256*
    • SHA-256
    • SHA-512
    • SHAKE-256, SHAKE128 algorithm with a 256-bit hash
    • SHAKE-512, SHAKE256 algorithm with a 512-bit hash
  • Password Hashes
    • Argon2id

Algorithms with an asterisk attached to them are the algorithms returned by Preferred*Type() calls.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrDecryptionFailure = errors.New("decryption failure")
View Source
var ErrInvalidCS = errors.New("invalid cryptostring")
View Source
var ErrUnsupportedAlgorithm = errors.New("unsupported algorithm")
View Source
var ErrVerificationFailure = errors.New("verification failure")

Functions

func CertifiedAlgorithmsRequired added in v0.2.0

func CertifiedAlgorithmsRequired() bool

Returns true if the library is using only certified algorithms

func CheckHash

func CheckHash(hash CryptoString, data []byte) (bool, error)

CheckHash generates a CryptoString hash of the supplied data

func GetAsymmetricAlgorithms added in v0.2.0

func GetAsymmetricAlgorithms() []string

Returns a list of supported asymmetric encryption algorithms

func GetHashAlgorithms added in v0.2.0

func GetHashAlgorithms() []string

Returns a list of supported hash algorithms

func GetSecretAlgorithms added in v0.2.0

func GetSecretAlgorithms() []string

Returns a list of supported symmetric encryption algorithms

func GetSigningAlgorithms added in v0.2.0

func GetSigningAlgorithms() []string

Returns a list of supported signing algorithms

func HashPassword

func HashPassword(password string) string

HashPassword turns a string into an Argon2 password hash.

func IsArgonHash

func IsArgonHash(hashstr string) (bool, error)

IsArgonHash checks to see if the string passed is an Argon2id password hash

func IsCertifiedAlgorithm added in v0.2.0

func IsCertifiedAlgorithm(algorithm string) bool

Returns true if the requested algorithm is one of the certified algorithms supported by the library. For those curious, this is as follows:

RSA2048-SHA256: 2048-bit RSA with SHA256 for internal hashing. PKCS #1, RFC 8017, and an allowed (but not approved) algorithm for key transport in FIPS 140-2.

ECDSA: Digital signatures using elliptic curve P-256 and SHA256 for internal hashing. FIPS 186-4 and SEC 1, Version 2.0

AES-128, AES-256: FIPS Publication 197.

SHA-256, SHA-512: 256-bit and 512-bit variants of the SHA2 hashing algorithm as documented in FIPS 180-4.

SHAKE-256, SHAKE-512: The SHAKE128 and SHAKE256 hashing algorithms, published in FIPS publication 202. Note that these algorithms require a 2x hash length to provide the corresponding 128 bits and 256 bits of security. The goeznacl prefixes denote the bit length of the hash, not the bits of security granted.

The ED25519 signature algorithm is an approved digital signature algorithm in the current draft of FIPS 186-5, but because the draft has not received final approval, it is not in this list.

As far as I am aware, there is no mention of Curve25519 asymmetric encryption as being allowed for key transport, but it is possible that it will achieve this status at some point in the future.

func PreferredAsymmetricType added in v0.2.0

func PreferredAsymmetricType() string

Returns the name of the library's recommended asymmetric encryption algorithm

func PreferredHashType added in v0.2.0

func PreferredHashType() string

Returns the name of the library's recommended hash algorithm

func PreferredSecretType added in v0.2.0

func PreferredSecretType() string

Returns the name of the library's recommended secret key algorithm

func PreferredSigningType added in v0.2.0

func PreferredSigningType() string

Returns the name of the library's recommended signing algorithm

func RequireCertifiedAlgorithms added in v0.2.0

func RequireCertifiedAlgorithms(require bool)

Tells the library to require (or not) certified algorithms, which defaults to not requiring them. Unless you have specific requirements to use this, don't. The recommended choices provide a balance of ease of use, speed, key size, and interoperability.

func VerifyPasswordHash

func VerifyPasswordHash(password string, hashPass string) (bool, error)

VerifyPasswordHash takes a password and the Argon2 hash to verify against, gets the parameters from the hash, applies them to the supplied password, and returns whether or not they match and if something went wrong

Types

type CryptoKey

type CryptoKey interface {
	GetEncryptionType() string
	GetType() string
}

CryptoKey is a baseline interface to the different kinds of keys defined in this module

type CryptoString

type CryptoString struct {
	Prefix string
	Data   string
}

func GetHash

func GetHash(algorithm string, data []byte) (CryptoString, error)

GetHash generates a CryptoString hash of the supplied data

func NewCS

func NewCS(str string) CryptoString

NewCS generates an instance from a string containing CryptoString data

func NewCSFromBytes

func NewCSFromBytes(algorithm string, buffer []byte) CryptoString

NewFromBytes creates a CryptoString object from an algorithm and buffer of data. The new instance makes a copy of the data buffer passed to it and Base85-encodes it for you. If you have an existing key or hash, this is probably what you want to use.

func (*CryptoString) AsBytes

func (cs *CryptoString) AsBytes() []byte

AsBytes returns the instance's prefix and encoded data as a byte slice

func (*CryptoString) AsString

func (cs *CryptoString) AsString() string

AsString returns the instance's prefix and encoded data as a string

func (*CryptoString) IsValid

func (cs *CryptoString) IsValid() bool

IsValid checks the internal data and returns True if it is valid

func (*CryptoString) MakeEmpty

func (cs *CryptoString) MakeEmpty()

MakeEmpty returns the object to an uninitialized state

func (*CryptoString) RawData

func (cs *CryptoString) RawData() []byte

RawData returns the raw, unencoded data of the object as a byte slice. In the event of an error, nil is returned

func (*CryptoString) Set

func (cs *CryptoString) Set(str string) error

Set takes a CryptoString-formatted string and sets the object to it.

func (*CryptoString) SetFromBytes

func (cs *CryptoString) SetFromBytes(algorithm string, buffer []byte) error

SetFromBytes assigns an algorithm and the associated data to the object. The caller retains ownership of the underlying data passed to it.

type DecryptorKey

type DecryptorKey interface {
	Decrypt(data string) ([]byte, error)
}

type EncryptionKey

type EncryptionKey struct {
	PublicHash CryptoString
	PublicKey  CryptoString
}

EncryptionKey is like EncryptionPair, but is just used for encryption and is equivalent to just the public key

func NewEncryptionKey

func NewEncryptionKey(pubkey CryptoString) *EncryptionKey

NewEncryptionKey creates a new EncryptionKey object from a CryptoString of the public key

func (EncryptionKey) Encrypt

func (ekey EncryptionKey) Encrypt(data []byte) (string, error)

Encrypt encrypts a byte slice using the internal public key. It returns the resulting encrypted data as a Base85-encoded string that amounts to a CryptoString without the prefix.

func (EncryptionKey) GetEncryptionType

func (ekey EncryptionKey) GetEncryptionType() string

GetEncryptionType returns the algorithm used by the key

func (EncryptionKey) GetType

func (ekey EncryptionKey) GetType() string

GetType returns the type of key -- asymmetric or symmetric

func (*EncryptionKey) Set

func (ekey *EncryptionKey) Set(pubkey CryptoString) error

Set assigns a CryptoString to the instance

type EncryptionPair

type EncryptionPair struct {
	PublicHash  CryptoString
	PrivateHash CryptoString
	PublicKey   CryptoString
	PrivateKey  CryptoString
}

EncryptionPair defines a pair of asymmetric encryption keys

func GenerateEncryptionPair

func GenerateEncryptionPair(algorithm string) (*EncryptionPair, error)

Generate creates a new EncryptionPair instance with a brand new set of keys

func NewEncryptionPair

func NewEncryptionPair(pubkey CryptoString, privkey CryptoString) *EncryptionPair

NewEncryptionPair creates a new EncryptionPair object from two CryptoString objects

func (EncryptionPair) Decrypt

func (kpair EncryptionPair) Decrypt(data string) ([]byte, error)

Decrypt decrypts a string of encrypted data which is Base85 encoded using the internal private key.

func (EncryptionPair) Encrypt

func (kpair EncryptionPair) Encrypt(data []byte) (string, error)

Encrypt encrypts a byte slice using the internal public key. It returns the resulting encrypted data as a Base85-encoded string that amounts to a CryptoString without the prefix.

func (EncryptionPair) GetEncryptionType

func (kpair EncryptionPair) GetEncryptionType() string

GetEncryptionType returns the algorithm used by the key

func (EncryptionPair) GetType

func (kpair EncryptionPair) GetType() string

GetType returns the type of key -- asymmetric or symmetric

func (*EncryptionPair) Set

func (kpair *EncryptionPair) Set(pubkey CryptoString, privkey CryptoString) error

Set assigns a pair of CryptoString values to the EncryptionPair

type EncryptorKey

type EncryptorKey interface {
	Encrypt(data []byte) (string, error)
}

type SecretKey

type SecretKey struct {
	Hash CryptoString
	Key  CryptoString
}

SecretKey defines a symmetric encryption key

func GenerateSecretKey

func GenerateSecretKey(algorithm string) *SecretKey

GenerateSecretKey creates a new SecretKey object with a randomly-generated key using a cryptographically safe method. This will return nil if given an invalid encryption algorithm

func NewSecretKey

func NewSecretKey(keyString CryptoString) *SecretKey

NewSecretKey creates a new NewSecretKey object from a CryptoString of the key. If given a bad key or one which uses an unsupported algorithm, it will return nil.

func (SecretKey) Decrypt

func (key SecretKey) Decrypt(data string) ([]byte, error)

Decrypt decrypts a string of encrypted data which is Base85 encoded using the internal key.

func (SecretKey) Encrypt

func (key SecretKey) Encrypt(data []byte) (string, error)

Encrypt encrypts a byte slice using the internal key. It returns the resulting encrypted data as a Base85-encoded string that amounts to a CryptoString without the prefix.

func (SecretKey) GetEncryptionType

func (key SecretKey) GetEncryptionType() string

GetEncryptionType returns the algorithm used by the key

func (SecretKey) GetType

func (key SecretKey) GetType() string

GetType returns the type of key -- asymmetric or symmetric

func (*SecretKey) Set

func (key *SecretKey) Set(keyString CryptoString) error

Set assigns a CryptoString value to the SecretKey

type SigningPair

type SigningPair struct {
	PublicHash  CryptoString
	PrivateHash CryptoString
	PublicKey   CryptoString
	PrivateKey  CryptoString
}

SigningPair defines an asymmetric signing key pair

func GenerateSigningPair

func GenerateSigningPair(algorithm string) (*SigningPair, error)

GenerateSigningPair creates a new instance with a randomly-generated key pair

func NewSigningPair

func NewSigningPair(pubkey CryptoString, privkey CryptoString) *SigningPair

NewSigningPair creates a new SigningPair object from two CryptoString objects

func (SigningPair) GetEncryptionType

func (spair SigningPair) GetEncryptionType() string

GetEncryptionType returns the algorithm used by the key

func (SigningPair) GetType

func (spair SigningPair) GetType() string

GetType returns the type of key -- asymmetric or symmetric

func (*SigningPair) Set

func (spair *SigningPair) Set(pubkey CryptoString, privkey CryptoString) error

Set assigns a pair of CryptoString values to the SigningPair

func (SigningPair) Sign

func (spair SigningPair) Sign(data []byte) (CryptoString, error)

Sign cryptographically signs a byte slice.

func (SigningPair) Verify

func (spair SigningPair) Verify(data []byte, signature CryptoString) (bool, error)

Verify uses the internal verification key with the passed data and signature and returns true if the signature has verified the data with that key.

type VerificationKey

type VerificationKey struct {
	PublicHash CryptoString
	// contains filtered or unexported fields
}

VerificationKey is an object to represent just a verification key, not a key pair

func NewVerificationKey

func NewVerificationKey(key CryptoString) *VerificationKey

NewVerificationKey creates a new verification key from a CryptoString

func (VerificationKey) GetEncryptionType

func (vkey VerificationKey) GetEncryptionType() string

GetEncryptionType returns the algorithm used by the key

func (VerificationKey) GetType

func (vkey VerificationKey) GetType() string

GetType returns the type of key -- asymmetric or symmetric

func (*VerificationKey) Set

func (vkey *VerificationKey) Set(key CryptoString) error

Set assigns a CryptoString value to the key

func (VerificationKey) Verify

func (vkey VerificationKey) Verify(data []byte, signature CryptoString) (bool, error)

Verify uses the internal verification key with the passed data and signature and returns true if the signature has verified the data with that key.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL