From 32a12e8249253abe4e5486a44ddee42f7b8c991d Mon Sep 17 00:00:00 2001 From: Dimitri Sokolyuk Date: Tue, 5 Jan 2016 20:33:22 +0100 Subject: Refactor --- account.go | 9 ++- cmd/acme/main.go | 4 +- provider.go | 209 ++++++++++++++++++++++++++++--------------------------- 3 files changed, 115 insertions(+), 107 deletions(-) diff --git a/account.go b/account.go index 62f1c78..46cc88c 100644 --- a/account.go +++ b/account.go @@ -4,6 +4,7 @@ import ( "crypto" "crypto/rsa" "encoding/base64" + "encoding/json" "io" "strings" @@ -31,7 +32,7 @@ func NewAccount(key *rsa.PrivateKey) (*Account, error) { // Signer describes a signing interface type Signer interface { - Sign([]byte, jose.NonceSource) (io.Reader, error) + Sign(interface{}, jose.NonceSource) (io.Reader, error) } type Thumber interface { @@ -44,7 +45,11 @@ type ThumbSigner interface { } // Sign implements Signer interface -func (a *Account) Sign(msg []byte, n jose.NonceSource) (io.Reader, error) { +func (a *Account) Sign(v interface{}, n jose.NonceSource) (io.Reader, error) { + msg, err := json.Marshal(v) + if err != nil { + return nil, err + } a.signer.SetNonceSource(n) obj, err := a.signer.Sign(msg) if err != nil { diff --git a/cmd/acme/main.go b/cmd/acme/main.go index 0fa0ddc..db39a0b 100644 --- a/cmd/acme/main.go +++ b/cmd/acme/main.go @@ -41,7 +41,7 @@ func chkKey(k PrivKey) (*rsa.PrivateKey, error) { } } -func saveCert(k Cert, crt *x509.Certificate) error { +func saveCert(k Cert, crt []*x509.Certificate) error { cert := k.CertPath() fd, err := mkdirCreate(cert, 0755, 0644) if err != nil { @@ -102,7 +102,7 @@ func main() { } log.Println(crt.NotBefore, crt.NotAfter) - err = saveCert(des, crt) + err = saveCert(des, []*x509.Certificate{crt}) if err != nil { log.Fatal("save cert", err) } diff --git a/provider.go b/provider.go index 040f713..0c896e7 100644 --- a/provider.go +++ b/provider.go @@ -8,12 +8,8 @@ import ( "io/ioutil" "log" "net/http" - "net/url" "regexp" - "strconv" "time" - - "github.com/mgutz/ansi" ) // Provider ... @@ -39,16 +35,15 @@ func (p Provider) Nonce() (string, error) { } } -func (p Provider) nonce(r *http.Response) { - if rn := r.Header.Get("Replay-Nonce"); rn != "" { - p.nonces <- rn - } -} - // NewProvider fetches directory and initializes nonce func NewProvider(directory string) (*Provider, error) { p := &Provider{nonces: make(chan string, 10)} - if _, err := p.get(directory, &p.Directory); err != nil { + resp, err := http.Get(directory) + if err != nil { + return nil, err + } + _, err = p.parse(resp, &p.Directory) + if err != nil { return nil, err } return p, nil @@ -73,47 +68,66 @@ func NewProvider(directory string) (*Provider, error) { // request is used for // new-reg, new-authz, challenge, new-cert + func (p *Provider) post(uri string, s Signer, v interface{}) (*http.Response, error) { - body, err := json.Marshal(v) + signed, err := s.Sign(v, p) if err != nil { return nil, err } - log.Println(ansi.Color("POST", "red+b"), uri, string(body)) + return http.Post(uri, "application/json", signed) +} - signed, err := s.Sign(body, p) - if err != nil { - return nil, err +type nextStep struct { + Link map[string]string + Location string +} + +var linksRe = regexp.MustCompile(`^<(.*)>;rel="(.*)"`) + +func (p *Provider) parse(resp *http.Response, v interface{}) (ns nextStep, err error) { + if lo, _ := resp.Location(); lo != nil { + log.Println(lo) + ns.Location = lo.String() + } + + ns.Link = make(map[string]string) + for _, li := range resp.Header["Link"] { + re := linksRe.FindStringSubmatch(li) + if len(re) == 3 { + ns.Link[re[2]] = re[1] + } + } + + if rn := resp.Header.Get("Replay-Nonce"); rn != "" { + p.nonces <- rn } - resp, err := http.Post(uri, "application/json", signed) + body, err := ioutil.ReadAll(resp.Body) if err != nil { - return nil, err + return ns, err } - p.nonce(resp) - log.Println(ansi.Color("STATUS", "yellow"), resp.Status) + defer resp.Body.Close() + + log.Println(string(body)) switch resp.Header.Get("Content-Type") { case "application/problem+json": - defer resp.Body.Close() - return resp, problem(resp) + var p Problem + if err := json.Unmarshal(body, &p); err != nil { + return ns, err + } + if err, ok := urnErrors[p.Type]; ok { + p.Err = err + } + err = p case "application/json": - defer resp.Body.Close() - return resp, json.NewDecoder(resp.Body).Decode(v) + err = json.Unmarshal(body, v) case "application/pkix-cert": - return resp, nil + v, err = x509.ParseCertificate(body) default: - return resp, errContentType + err = errContentType } -} - -func (p *Provider) get(uri string, v interface{}) (*http.Response, error) { - resp, err := http.Get(uri) - if err != nil { - return nil, err - } - defer resp.Body.Close() - p.nonce(resp) - return resp, json.NewDecoder(resp.Body).Decode(v) + return ns, err } func problem(resp *http.Response) error { @@ -127,40 +141,6 @@ func problem(resp *http.Response) error { return p } -type nextStep struct { - Link map[string]string - Location *url.URL - RetryAfter time.Duration -} - -var linksRe = regexp.MustCompile(`^<(.*)>;rel="(.*)"`) - -func parseHeader(r *http.Response) nextStep { - var ns nextStep - - if lo, err := r.Location(); err == nil { - ns.Location = lo - } - - ns.Link = make(map[string]string) - for _, li := range r.Header["Link"] { - re := linksRe.FindStringSubmatch(li) - if len(re) == 3 { - ns.Link[re[2]] = re[1] - } - } - - if ra := r.Header.Get("Retry-After"); ra != "" { - n, err := strconv.Atoi(ra) - if err == nil { - ns.RetryAfter = time.Second * time.Duration(n) - } - } - - log.Println(ansi.Color("NEXT", "cyan"), ns) - return ns -} - /* directory . @@ -185,31 +165,34 @@ func (p *Provider) Register(s Signer, c Contacts) error { Resource: ResNewReg, Contact: c, } + resp, err := p.post(p.NewReg, s, r) + if err != nil { + return err + } + + ns, err := p.parse(resp, r) + if err != nil && err.(Problem).Err != ErrMalformed { return err } - ns := parseHeader(resp) - switch resp.StatusCode { - case http.StatusConflict: - // Query Location - r = &Registration{Resource: ResReg} - resp, err = p.post(ns.Location.String(), s, r) - if err != nil { - return err - } - fallthrough - case http.StatusCreated: - // Agree to TOS - if tos := ns.Link["terms-of-service"]; tos != "" { - r = &Registration{ - Resource: ResReg, - Contact: c, - Agreement: tos, - } - _, err = p.post(ns.Location.String(), s, r) - } + + r = &Registration{ + Resource: ResReg, + Contact: c, + } + + if tos := ns.Link["terms-of-service"]; tos != "" { + r.Agreement = tos + } + + resp, err = p.post(ns.Location, s, r) + if err != nil { + return err } + + _, err = p.parse(resp, r) + return err } @@ -227,10 +210,16 @@ func (p *Provider) Authorize(s ThumbSigner, domain string) error { Resource: ResNewAuthz, Identifier: NewIdent(domain), } + resp, err := p.post(p.NewAuthz, s, r) if err != nil { return err } + _, err = p.parse(resp, r) + if err != nil { + return err + } + switch resp.StatusCode { case http.StatusCreated: n, ch := pickChallenge(r.Challenges) @@ -244,7 +233,16 @@ func (p *Provider) Authorize(s ThumbSigner, domain string) error { Type: ch.Type, KeyAuthorization: ka, } - _, err = p.post(ch.URI, s, ans) + + resp, err = p.post(ch.URI, s, ans) + if err != nil { + return err + } + + ns, err := p.parse(resp, ans) + if err != nil { + return err + } var sol Solver @@ -259,15 +257,14 @@ func (p *Provider) Authorize(s ThumbSigner, domain string) error { return err } - ns := parseHeader(resp) done := make(chan bool) errc := make(chan error) - log.Println(ansi.Color("NEXT", "green"), ns) + ticker := time.NewTicker(time.Second) defer ticker.Stop() go func() { for range ticker.C { - err := p.queryStatus(ns.Location.String(), n, done) + err := p.queryStatus(ns.Location, done) if err != nil { errc <- err return @@ -285,12 +282,17 @@ func (p *Provider) Authorize(s ThumbSigner, domain string) error { return err } -func (p *Provider) queryStatus(url string, n int, done chan bool) error { - r := &Authorization{} - if _, err := p.get(url, r); err != nil { +func (p *Provider) queryStatus(url string, done chan bool) error { + r := &Challenge{} + resp, err := http.Get(url) + if err != nil { return err } - if r.Challenges[n].Status == StatusValid { + _, err = p.parse(resp, r) + if err != nil { + return err + } + if r.Status == StatusValid { done <- true } return nil @@ -305,16 +307,17 @@ func (p *Provider) Cert(s Signer, altnames []string, key *rsa.PrivateKey) (*x509 Resource: ResNewCert, CSR: csr, } + resp, err := p.post(p.NewCert, s, r) if err != nil { return nil, err } - defer resp.Body.Close() - ns := parseHeader(resp) - log.Println(ansi.Color("NEXT", "green"), ns) - der, err := ioutil.ReadAll(resp.Body) + + crt := new(x509.Certificate) + _, err = p.parse(resp, crt) if err != nil { return nil, err } - return x509.ParseCertificate(der) + + return crt, nil } -- cgit v1.2.3