| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102 |
- package acme
- import (
- "crypto/sha256"
- "encoding/base64"
- "errors"
- "fmt"
- "net/http"
- "time"
- )
- // EncodeDNS01KeyAuthorization encodes a key authorization and provides a value to be put in the TXT record for the _acme-challenge DNS entry.
- func EncodeDNS01KeyAuthorization(keyAuth string) string {
- h := sha256.Sum256([]byte(keyAuth))
- return base64.RawURLEncoding.EncodeToString(h[:])
- }
- // Helper function to determine whether a challenge is "finished" by its status.
- func checkUpdatedChallengeStatus(challenge Challenge) (bool, error) {
- switch challenge.Status {
- case "pending":
- // Challenge objects are created in the "pending" state.
- // TODO: https://github.com/letsencrypt/boulder/issues/3346
- // return true, errors.New("acme: unexpected 'pending' challenge state")
- return false, nil
- case "processing":
- // They transition to the "processing" state when the client responds to the
- // challenge and the server begins attempting to validate that the client has completed the challenge.
- return false, nil
- case "valid":
- // If validation is successful, the challenge moves to the "valid" state
- return true, nil
- case "invalid":
- // if there is an error, the challenge moves to the "invalid" state.
- if challenge.Error.Type != "" {
- return true, challenge.Error
- }
- return true, errors.New("acme: challenge is invalid, no error provided")
- default:
- return true, fmt.Errorf("acme: unknown challenge status: %s", challenge.Status)
- }
- }
- // UpdateChallenge responds to a challenge to indicate to the server to complete the challenge.
- func (c Client) UpdateChallenge(account Account, challenge Challenge) (Challenge, error) {
- resp, err := c.post(challenge.URL, account.URL, account.PrivateKey, struct{}{}, &challenge, http.StatusOK)
- if err != nil {
- return challenge, err
- }
- if loc := resp.Header.Get("Location"); loc != "" {
- challenge.URL = loc
- }
- challenge.AuthorizationURL = fetchLink(resp, "up")
- if finished, err := checkUpdatedChallengeStatus(challenge); finished {
- return challenge, err
- }
- pollInterval, pollTimeout := c.getPollingDurations()
- end := time.Now().Add(pollTimeout)
- for {
- if time.Now().After(end) {
- return challenge, errors.New("acme: challenge update timeout")
- }
- time.Sleep(pollInterval)
- resp, err := c.post(challenge.URL, account.URL, account.PrivateKey, "", &challenge, http.StatusOK)
- if err != nil {
- // i don't think it's worth exiting the loop on this error
- // it could just be connectivity issue that's resolved before the timeout duration
- continue
- }
- if loc := resp.Header.Get("Location"); loc != "" {
- challenge.URL = loc
- }
- challenge.AuthorizationURL = fetchLink(resp, "up")
- if finished, err := checkUpdatedChallengeStatus(challenge); finished {
- return challenge, err
- }
- }
- }
- // FetchChallenge fetches an existing challenge from the given url.
- func (c Client) FetchChallenge(account Account, challengeURL string) (Challenge, error) {
- challenge := Challenge{}
- resp, err := c.post(challengeURL, account.URL, account.PrivateKey, "", &challenge, http.StatusOK)
- if err != nil {
- return challenge, err
- }
- challenge.URL = resp.Header.Get("Location")
- challenge.AuthorizationURL = fetchLink(resp, "up")
- return challenge, nil
- }
|