package nosql import ( "context" "encoding/base64" "time" "github.com/pkg/errors" "github.com/smallstep/certificates/acme" "github.com/smallstep/nosql" "github.com/smallstep/nosql/database" ) // dbNonce contains nonce metadata used in the ACME protocol. type dbNonce struct { ID string CreatedAt time.Time DeletedAt time.Time } // CreateNonce creates, stores, and returns an ACME replay-nonce. // Implements the acme.DB interface. func (db *DB) CreateNonce(ctx context.Context) (acme.Nonce, error) { _id, err := randID() if err != nil { return "", err } id := base64.RawURLEncoding.EncodeToString([]byte(_id)) n := &dbNonce{ ID: id, CreatedAt: clock.Now(), } if err := db.save(ctx, id, n, nil, "nonce", nonceTable); err != nil { return "", err } return acme.Nonce(id), nil } // DeleteNonce verifies that the nonce is valid (by checking if it exists), // and if so, consumes the nonce resource by deleting it from the database. func (db *DB) DeleteNonce(_ context.Context, nonce acme.Nonce) error { err := db.db.Update(&database.Tx{ Operations: []*database.TxEntry{ { Bucket: nonceTable, Key: []byte(nonce), Cmd: database.Get, }, { Bucket: nonceTable, Key: []byte(nonce), Cmd: database.Delete, }, }, }) switch { case nosql.IsErrNotFound(err): return acme.NewError(acme.ErrorBadNonceType, "nonce %s not found", string(nonce)) case err != nil: return errors.Wrapf(err, "error deleting nonce %s", string(nonce)) default: return nil } }