Black Friday Sale!
Save 50% on Web Development with Go and Test with Go until Dec 3.
Both courses have lifetime access, a 30-day money back guarantee, and free updates. This is also the biggest discount I offer on the courses, and I only do it once a year around Black Friday.
Thank you for your continued support and happy coding!
Jon Calhoun
When writing code in Go, or really any language, you will often find yourself using other packages to get things done. For example, you might use the net/http
package as a basis for building a web server rather than writing all that code yourself.
From time to time you might notice that the third party package doesn’t quite behave in a manner that is aligned with how you intend to use the package. One example of this is the golang.org/x/crypto/bcrypt
package; When I use it, I am often dealing with strings in my own application, but every method in the crypto/bcrypt
package expects a byte slice as input. While this isn’t necessarily a problem, it can lead to code duplication that is unnecessary.
Another frequent occurrence is wanting to add some functionality on top of what is being offered by another package. Looking at the crypto/bcrypt
package once again, this is a package that handles hashing and salting passwords, but it would be especially great if the package took care of peppering our passwords as well.
In this article we are going to explore a technique that can be used to simplifying your code that interacts with third party libraries by wrapping those packages into your own package. We will be doing this using the crypto/bcrypt
package, but the same pattern can be applied to other packages and types.
Ready to master Go?
Do you want to level up your Go skills? Check out my FREE course, Gophercises where we complete mini-exercises that are each designed to teach you unique Go skills. You’ll also have a lot of fun to boot 😀
bcrypt
packageBefore we can jump into wrapping the crypto/bcrypt
package we first need to take a look at how you might use it inside of an existing application. To do this, we are going to look at two different functions provided by the crypto/bcrypt
package:
GenerateFromPassword
- given a raw password in the form of a byte slice and a cost, this functions generates a hashed and salted password using the bcrypt algorithm.CompareHashAndPassword
- given a hashed password and a raw password, both as byte slices, this compares the two passwords in a time-constant manner and returns an error if they don’t match for any reason.We are also going to assume that our application is making use of a pepper
variable, which is similar to a salt but is application-wide and is intended to help prevent attackers from getting passwords when they only get access to a database and not the actual source code. We will also assume that we are dealing with strings, so both the incoming raw password and the final hashed password will need to be strings.
Putting that all together, you might end up with some code like below to generate your hashed password.
rawPassword := "some-pw"
pepper := "some-pepper"
hashedBytes, err := bcrypt.GenerateFromPassword(
[]byte(rawPassword + pepper), bcrypt.DefaultCost)
if err != nil {
// This really shouldn't happen unless we provide an invalid bcrypt cost or
// are doing something else very wrong
}
// Use the hashedBytes, or convert to a string
hashedString := string(hashedBytes)
And then you might write something like the code below to compare your password to a hashed one. For simplicity’s sake, we are just assigning the hashed password to a string, but in most practical cases you would get this from a datastore of some sort.
rawPw := "some-pw"
pepper := "some-pepper"
hashedPw := "$2a$10$aSjYOncUeas4wV3e5vjb8eBT4lKrev3vMjvTATAYlGqY3m6RHnHFa"
err := bcrypt.CompareHashAndPassword(
[]byte(hashedPw),
[]byte(rawPw+pepper))
if err != nil {
// Passwords don't match - tell the user the login was invalid!
}
// The passwords match if no error!
This code sample isn’t complete; it is only meant to give you an idea of how you would use the CompareHashAndPassword()
function.
Now that we know what we are working with we can start to look for ways to wrap the crypto/bcrypt
package and simplify our code.
The first thing I suggest doing is looking for common tasks that you are performing in order to utilize the package. In this case, it looks like we are converting between byte slices and strings a good bit. Another thing we are doing is messing with a pepper a lot - when we create and compare passwords we are appending a pepper to the strings every time. One final thing that we might consider is changing the return value of the the CompareHashAndPassword()
function; I often end up treating this value as a true/false statement (checking if it is nil), so we will be updating that function to return a bool
rather than an error.
Now that we know have an idea of what code we are writing a lot we can start to look at how we might simplify this.
It is important to note that we didn’t try to simplify this first! We first wrote the code that uses the bcrypt
package, and then once we had a realistic idea of what we could do simplify we took some notes and then proceeded to refactor our code.
The first thing we are going to do is declare a new type inside of our own hash
package. In many cases you won’t need to create a new type and can instead just offer functions to get the job done, but in this case I feel that a struct is more appropriate because we are going to store a pepper in the struct. Doing this will make it much easier to just pass around a copy of our BCrypt
object to code that needs to handle passwords, and they won’t have to deal with a pepper directly.
package hash
// We will use this shortly, so I am showing it for now.
import "golang.org/x/crypto/bcrypt"
func NewBCrypt(pepper string) BCrypt {
return BCrypt{[]byte(pepper)}
}
type BCrypt struct {
pepper []byte
}
I also created a NewBCrypt()
function to be used when constructing a new BCrypt
object, which handles converting the incoming pepper string into a byte slice. Again, this is useful because in my application I typically tend to be working with strings in stead of byte slices for my pepper.
Next we will implement a function used to compare a raw password with a hashed password. In the crypto/bcrypt
package this function is titled CompareHashAndPassword()
, but we are free to name it whatever we want, and I tend to prefer using something a little shorter like Equal()
. As I mentioned before, this will return a boolean instead of an error.
func (bc BCrypt) Equal(hash, rawPw string) bool {
err := bcrypt.CompareHashAndPassword(
[]byte(hash),
append([]byte(rawPw), bc.pepper...))
if err == nil {
return true
}
if err == bcrypt.ErrMismatchedHashAndPassword {
return false
}
// If this happens it means our password hash isn't valid
// or some other unrecoverable error happened.
panic(err)
}
Obviously this isn’t the most performant code in the world because it has to convert strings to bytes, but if we are already working with strings in our application chances are we would need to do this anyway, and this does make dealing with our strings much easier.
Moving on, we are going to create a method used to hash our passwords - aptly named Hash()
. Once again, this will deal with strings instead of byte slices, but in this case we will still return any errors that occur because our upstrem code will likely need to know about them.
func (bc BCrypt) Hash(input string) (string, error) {
bytes, err := bcrypt.GenerateFromPassword(
append([]byte(input), bc.pepper...),
bcrypt.DefaultCost)
if err != nil {
return "", err
}
return string(bytes), nil
}
Now that we have our new BCrypt
type written, let’s go back to our original code and see how we might use this to simplify our original code.
The first thing we need to do is construct a new instance of our BCrypt
type. That will give us the ability to simply pass this around instead of passing around our pepper.
bc := NewBCrypt("some-pepper")
After that we can utilize the bc
object to hash our password.
rawPw := "some-pw"
hashedPw, err := bc.Hash(rawPw)
if err != nil {
panic(err)
}
And finally we can use the bc
object to compare our hashed password with a raw password to determine if the user has provided us with valid credentials.
if bc.Equal(hashedPw, rawPw) {
fmt.Println("They match!")
}
Putting it all together we get something like the code below.
package main
import (
"fmt"
"calhoun.io/hash"
)
func main() {
bc := hash.NewBCrypt("some-pepper")
rawPw := "some-pw"
hashedPw, err := bc.Hash(rawPw)
if err != nil {
panic(err)
}
if bc.Equal(hashedPw, rawPw) {
fmt.Println("They match!")
}
}
Now that is much cleaner. Our code no longer has to think about a bcrypt cost, adding a pepper to passwords, or even converting strings to byte slices. This is really important for maintaining code in because it means that any future changes that might alter the cost you are using for bcrypt won’t need to be spread across multiple files, but instead could be a single change to the BCrypt
type. You could also make the cost an argument when calling NewBCrypt
.
As I said before, you can do this for a large variety of packages. For example, you might find that you need to use HMAC in your code, but you need to store a base64 encoded version of the HMACs rather than a byte slice. That is easily achieved with the code below.
package hash
import (
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"hash"
)
func NewHMAC(key string) HMAC {
h := hmac.New(sha256.New, []byte(key))
return HMAC{h}
}
type HMAC struct {
hash.Hash
}
func (h HMAC) Equal(hash, raw string) bool {
hashMac, err := base64.URLEncoding.DecodeString(hash)
if err != nil {
// Invalid MAC provided
return false
}
rawMac := h.Bytes([]byte(raw))
return hmac.Equal(hashMac, rawMac)
}
func (h HMAC) Bytes(input []byte) []byte {
h.Reset()
h.Write(input)
return h.Sum(nil)
}
func (h HMAC) String(input string) string {
// Leverage the existing Bytes() method
return base64.URLEncoding.EncodeToString(h.Bytes([]byte(input)))
}
Or you might have a custom type and decide that you want to make sorting slices of that type easier. No problemo! We don’t even have to export a new type to achieve this.
package sort
import (
"sort"
"calhoun.io/things"
)
// calhoun.io/things defines the Dog type:
//
// type Dog struct {
// Age int
// }
func Dogs(a []things.Dog) {
sort.Sort(dogs(a))
}
type dogs []things.Dog
func (d dogs) Len() int {
return len(d)
}
func (d dogs) Less(i, j int) bool {
return d[i].Age < d[j].Age
}
func (d dogs) Swap(i, j int) {
d[i], d[j] = d[j], d[i]
}
The next time you finish writing some code, I encourage you to take a moment to consider how you could clean up your code and make it more maintainable.
While this pattern not always be a great fit, it is a great way to help isolate responsibilities inside your application, which typically leads to a more maintainable codebase.
Sign up for my mailing list and I'll send you a FREE sample from my course - Web Development with Go. The sample includes 19 screencasts and the first few chapters from the book.
You will also receive emails from me about Go coding techniques, upcoming courses (including FREE ones), and course discounts.
Jon Calhoun is a full stack web developer who teaches about Go, web development, algorithms, and anything programming. If you haven't already, you should totally check out his Go courses.
Previously, Jon worked at several statups including co-founding EasyPost, a shipping API used by several fortune 500 companies. Prior to that Jon worked at Google, competed at world finals in programming competitions, and has been programming since he was a child.
Related articles
Spread the word
Did you find this page helpful? Let others know about it!
Sharing helps me continue to create both free and premium Go resources.
Want to discuss the article?
See something that is wrong, think this article could be improved, or just want to say thanks? I'd love to hear what you have to say!
You can reach me via email or via twitter.
©2018 Jonathan Calhoun. All rights reserved.