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 }