route/database/certcache.go

106 lines
2.7 KiB
Go

package database
import (
"errors"
"log"
r "github.com/GoRethink/gorethink"
"github.com/brandur/simplebox"
"golang.org/x/crypto/acme/autocert"
"golang.org/x/net/context"
)
// CertCache extends DB to provide certificate management functions for
// https://godoc.org/golang.org/x/crypto/acme/autocert#Cache
type CertCache struct {
*DB
SimpleBox *simplebox.SimpleBox
}
// CryptoLevel indicates what form of cryptography the certificate is stored
// with.
type CryptoLevel int
// Crypto levels / strategies defined
const (
// NOTE: this is defined for debugging / testing usage only
CryptoLevelNone CryptoLevel = iota
// Use the global set of secretbox keys
CryptoLevelSecretbox
)
// CachedCert is an individual cached certificate in the database.
type CachedCert struct {
Key string `gorethink:"id" storm:"id"`
CryptoLevel CryptoLevel `gorethink:"cryptoLevel"`
// PEM-encoded bytes with the above crypto level as a filter.
Body []byte `gorethink:"body"`
}
// Get returns a certificate data for the specified key.
// If there's no such key, Get returns ErrCacheMiss.
func (c *CertCache) Get(ctx context.Context, key string) ([]byte, error) {
res, err := r.Table("certs").Get(key).Run(c.s)
if err != nil {
return nil, err
}
cert := &CachedCert{}
err = res.One(cert)
if err != nil {
log.Printf("decoding cached cert failed: %v", err)
return nil, autocert.ErrCacheMiss
}
var body []byte
switch cert.CryptoLevel {
case CryptoLevelNone:
body = cert.Body
case CryptoLevelSecretbox:
if c.SimpleBox == nil {
return nil, errors.New("can't read this cert, no key in memory")
}
body, err = c.SimpleBox.Decrypt(cert.Body)
if err != nil {
return nil, autocert.ErrCacheMiss
}
}
log.Printf("certcache: fetched: %s", key)
return body, nil
}
// Put stores the data in the cache under the specified key.
// Underlying implementations may use any data storage format,
// as long as the reverse operation, Get, results in the original data.
func (c *CertCache) Put(ctx context.Context, key string, data []byte) error {
cert := &CachedCert{
Key: key,
CryptoLevel: CryptoLevelNone,
Body: data,
}
if c.SimpleBox != nil {
cert.CryptoLevel = CryptoLevelSecretbox
cert.Body = c.SimpleBox.Encrypt(data)
}
log.Printf("certcache: added: %s", key)
_, err := r.Table("certs").Insert(cert).RunWrite(c.s)
return err
}
// Delete removes a certificate data from the cache under the specified key.
// If there's no such key in the cache, Delete returns nil.
func (c *CertCache) Delete(ctx context.Context, key string) error {
_, err := r.Table("certs").Get(key).Delete().Run(c.s)
log.Printf("certcache: deleted: %s", key)
return err
}