aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDimitri Sokolyuk <demon@dim13.org>2016-01-31 23:15:27 +0100
committerDimitri Sokolyuk <demon@dim13.org>2016-01-31 23:15:27 +0100
commitc49a0bf48ea10b54dbac3480716d9786a0ce8411 (patch)
tree3ce88f2422f3484aa95f77669fdafdfb6b2e63d9
parentc9038d2f35945774f6cd32ade7b3abd1cd5ba952 (diff)
Split files (experimental)
-rw-r--r--authorize.go79
-rw-r--r--certificate.go42
-rw-r--r--challenge.go90
-rw-r--r--messages.go143
-rw-r--r--provider.go188
-rw-r--r--register.go48
6 files changed, 301 insertions, 289 deletions
diff --git a/authorize.go b/authorize.go
new file mode 100644
index 0000000..0dc82a2
--- /dev/null
+++ b/authorize.go
@@ -0,0 +1,79 @@
+package acme
+
+import "time"
+
+// Authorization request
+type Authorization struct {
+ Resource Resource `json:"resource"` // new-authz
+ Identifier Identifier `json:"identifier"`
+ Status Status `json:"status,omitempty"` // e.g. valid
+ Expires *time.Time `json:"expires,omitempty"`
+ Challenges []Challenge `json:"challenges,omitempty"`
+ Combinations [][]int `json:"combinations,omitempty"`
+}
+
+// Identifier ...
+type Identifier struct {
+ Type IdentType `json:"type"` // dns
+ Value string `json:"value"` // example.com
+}
+
+type IdentType string
+
+const IdentDNS IdentType = "dns"
+
+func (a Authorization) Supported(sol map[ChalType]Solver) []Challenge {
+ supported := func(com []int) bool {
+ for _, n := range com {
+ if _, ok := sol[a.Challenges[n].Type]; !ok {
+ return false
+ }
+ }
+ return true
+ }
+ for _, com := range a.Combinations {
+ if supported(com) {
+ c := make([]Challenge, len(com))
+ for i, n := range com {
+ c[i] = a.Challenges[n]
+ }
+ return c
+ }
+ }
+ return nil
+}
+
+func (p *Provider) authz(s Signer, domain string, sol map[ChalType]Solver) error {
+ // first step: pocke
+ req := &Authorization{
+ Resource: ResNewAuthz,
+ Identifier: Identifier{
+ Type: IdentDNS,
+ Value: domain,
+ },
+ }
+ resp, err := p.post(p.NewAuthz, s, req)
+ if err != nil {
+ return err
+ }
+ err = parseJson(resp, req)
+ if err != nil {
+ return err
+ }
+ // second step: choose and start solver
+ for _, ch := range req.Supported(sol) {
+ if err = p.solve(s, ch, sol[ch.Type]); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (p *Provider) Authorize(s Signer, d *Desire) error {
+ for _, domain := range d.altnames {
+ if err := p.authz(s, domain, d.solver); err != nil {
+ return err
+ }
+ }
+ return nil
+}
diff --git a/certificate.go b/certificate.go
new file mode 100644
index 0000000..4421061
--- /dev/null
+++ b/certificate.go
@@ -0,0 +1,42 @@
+package acme
+
+import "crypto/x509"
+
+type CSR struct {
+ Resource Resource `json:"resource"` // new-cert
+ CSR string `json:"csr"`
+}
+
+func (p *Provider) Bundle(s Signer, d *Desire) error {
+ d.cert = make([]*x509.Certificate, 2)
+ // first step: post csr
+ csr, err := d.CSR()
+ if err != nil {
+ return err
+ }
+ req := &CSR{
+ Resource: ResNewCert,
+ CSR: csr,
+ }
+ resp, err := p.post(p.NewCert, s, req)
+ if err != nil {
+ return err
+ }
+ d.cert[0], err = parseCert(resp)
+ if err != nil {
+ return err
+ }
+ ns := parseHeader(resp)
+
+ // second step: cet CA
+ resp, err = p.Get(ns.Link["up"])
+ if err != nil {
+ return err
+ }
+ d.cert[1], err = parseCert(resp)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
diff --git a/challenge.go b/challenge.go
new file mode 100644
index 0000000..c39bc12
--- /dev/null
+++ b/challenge.go
@@ -0,0 +1,90 @@
+package acme
+
+import (
+ "log"
+ "time"
+)
+
+// Challege ...
+type Challenge struct {
+ Resource Resource `json:"resource"` // challenge
+ Type ChalType `json:"type"`
+ Token string `json:"token,omitempty"`
+ Status Status `json:"status,omitempty"` // e.g. valid
+ URI string `json:"uri,omitempty"`
+ Validated *time.Time `json:"validated,omitempty"`
+ KeyAuthorization string `json:"keyAuthorization,omitempty"`
+ Err *Problem `json:"error,omitempty"`
+}
+
+// Status of request
+type Status string
+
+// Statuses
+const (
+ StatusUnknown Status = "unknown"
+ StatusPending Status = "pending"
+ StatusProcessing Status = "processing"
+ StatusValid Status = "valid"
+ StatusInvalid Status = "invalid"
+ StatusRevoked Status = "revoked"
+)
+
+type ChalType string
+
+const (
+ ChallengeHTTP ChalType = "http-01"
+ ChallengeTLS ChalType = "tls-sni-01"
+ ChallengePOP ChalType = "proofOfPossession-01"
+ ChallengeDNS ChalType = "dns-01"
+)
+
+func (p *Provider) solve(s Signer, ch Challenge, sol Solver) error {
+ ka, err := s.KeyAuth(ch.Token)
+ if err != nil {
+ return err
+ }
+
+ // prepare solver
+ err = sol.Solve(ch.Token, ka)
+ if err != nil {
+ return err
+ }
+ defer sol.Solved()
+
+ // update challenge
+ ch.Resource = ResChallenge
+ ch.KeyAuthorization = ka
+
+ resp, err := p.post(ch.URI, s, ch)
+ if err != nil {
+ return err
+ }
+ ns := parseHeader(resp)
+
+ return p.pollStatus(ns.Location)
+}
+
+func (p *Provider) pollStatus(uri string) error {
+ t := time.NewTicker(poll)
+ defer t.Stop()
+ for range t.C {
+ resp, err := p.Get(uri)
+ if err != nil {
+ return err
+ }
+ req := new(Challenge)
+ err = parseJson(resp, req)
+ if err != nil {
+ return err
+ }
+ if req.Err != nil {
+ return req.Err
+ }
+ log.Println("status", req.Status)
+ if req.Status == StatusValid {
+ return nil
+ }
+ }
+ return nil
+}
diff --git a/messages.go b/messages.go
deleted file mode 100644
index c462c1d..0000000
--- a/messages.go
+++ /dev/null
@@ -1,143 +0,0 @@
-package acme
-
-import (
- "net"
- "time"
-
- "github.com/square/go-jose"
-)
-
-const (
- // LE1 Let's Encrypt V1
- LE1 = `https://acme-v01.api.letsencrypt.org/directory`
- // LES Let's Encrypt Staging
- LES = `https://acme-staging.api.letsencrypt.org/directory`
-)
-
-// Directory ...
-type Directory struct {
- NewReg string `json:"new-reg"`
- RecoverReg string `json:"recover-reg"`
- NewAuthz string `json:"new-authz"`
- NewCert string `json:"new-cert"`
- RevokeCert string `json:"revoke-cert"`
-}
-
-// Registration Objects
-type Registration struct {
- Resource Resource `json:"resource"` // new-reg
- Contact Contacts `json:"contact,omitempty"`
- Agreement string `json:"agreement,omitempty"`
- Authorizations string `json:"authorizations,omitempty"`
- Certificates string `json:"certificates,omitempty"`
- ID int `json:"id,omitempty"`
- Key *jose.JsonWebKey `json:"key,omitempty"`
- InitialIP *net.IP `json:"initialIp,omitempty"` // not in draft
- CreatedAt *time.Time `json:"createdAt,omitempty"`
-}
-
-// Authorization request
-type Authorization struct {
- Resource Resource `json:"resource"` // new-authz
- Identifier Identifier `json:"identifier"`
- Status Status `json:"status,omitempty"` // e.g. valid
- Expires *time.Time `json:"expires,omitempty"`
- Challenges []Challenge `json:"challenges,omitempty"`
- Combinations [][]int `json:"combinations,omitempty"`
-}
-
-func (a Authorization) Supported(sol map[ChalType]Solver) []Challenge {
- supported := func(com []int) bool {
- for _, n := range com {
- if _, ok := sol[a.Challenges[n].Type]; !ok {
- return false
- }
- }
- return true
- }
- for _, com := range a.Combinations {
- if supported(com) {
- c := make([]Challenge, len(com))
- for i, n := range com {
- c[i] = a.Challenges[n]
- }
- return c
- }
- }
- return nil
-}
-
-// Identifier ...
-type Identifier struct {
- Type IdentType `json:"type"` // dns
- Value string `json:"value"` // example.com
-}
-
-// Challege ...
-type Challenge struct {
- Resource Resource `json:"resource"` // challenge
- Type ChalType `json:"type"`
- Token string `json:"token,omitempty"`
- Status Status `json:"status,omitempty"` // e.g. valid
- URI string `json:"uri,omitempty"`
- Validated *time.Time `json:"validated,omitempty"`
- KeyAuthorization string `json:"keyAuthorization,omitempty"`
- Err *Problem `json:"error,omitempty"`
-}
-
-// Problem description
-type Problem struct {
- Type string `json:"type"`
- Detail string `json:"detail"`
- Instance string `json:"instance"`
- Err error `json:"-"`
-}
-
-func (p Problem) Error() string {
- return p.Detail
-}
-
-// Status of request
-type Status string
-
-// Statuses
-const (
- StatusUnknown Status = "unknown"
- StatusPending Status = "pending"
- StatusProcessing Status = "processing"
- StatusValid Status = "valid"
- StatusInvalid Status = "invalid"
- StatusRevoked Status = "revoked"
-)
-
-type Resource string
-
-const (
- ResNewReg Resource = "new-reg"
- ResRecoverReg Resource = "recover-reg"
- ResNewAuthz Resource = "new-authz"
- ResNewCert Resource = "new-cert"
- ResRevokeCert Resource = "revoke-cert"
- ResReg Resource = "reg"
- ResAuthz Resource = "authz"
- ResChallenge Resource = "challenge"
- ResCert Resource = "cert"
-)
-
-type IdentType string
-
-const IdentDNS IdentType = "dns"
-
-type ChalType string
-
-const (
- ChallengeHTTP ChalType = "http-01"
- ChallengeTLS ChalType = "tls-sni-01"
- ChallengePOP ChalType = "proofOfPossession-01"
- ChallengeDNS ChalType = "dns-01"
-)
-
-type CSR struct {
- Resource Resource `json:"resource"` // new-cert
- CSR string `json:"csr"`
-}
diff --git a/provider.go b/provider.go
index 7cda357..69d125a 100644
--- a/provider.go
+++ b/provider.go
@@ -4,12 +4,49 @@ import (
"crypto/x509"
"encoding/json"
"errors"
- "log"
"net/http"
"regexp"
"time"
)
+const (
+ // LE1 Let's Encrypt V1
+ LE1 = `https://acme-v01.api.letsencrypt.org/directory`
+ // LES Let's Encrypt Staging
+ LES = `https://acme-staging.api.letsencrypt.org/directory`
+)
+
+type Resource string
+
+const (
+ ResNewReg Resource = "new-reg"
+ ResRecoverReg Resource = "recover-reg"
+ ResNewAuthz Resource = "new-authz"
+ ResNewCert Resource = "new-cert"
+ ResRevokeCert Resource = "revoke-cert"
+ ResReg Resource = "reg"
+ ResAuthz Resource = "authz"
+ ResChallenge Resource = "challenge"
+ ResCert Resource = "cert"
+)
+
+// Directory ...
+type Directory struct {
+ NewReg string `json:"new-reg"`
+ RecoverReg string `json:"recover-reg"`
+ NewAuthz string `json:"new-authz"`
+ NewCert string `json:"new-cert"`
+ RevokeCert string `json:"revoke-cert"`
+}
+
+// Problem description
+type Problem struct {
+ Type string `json:"type"`
+ Detail string `json:"detail"`
+ Instance string `json:"instance"`
+ Err error `json:"-"`
+}
+
// Provider ...
type Provider struct {
Directory
@@ -34,6 +71,10 @@ const (
poll = time.Second
)
+func (p Problem) Error() string {
+ return p.Detail
+}
+
// RoundTrip implements RoundTipper
func (p Provider) RoundTrip(req *http.Request) (*http.Response, error) {
resp, err := p.Transport.RoundTrip(req)
@@ -130,148 +171,3 @@ func parseCert(resp *http.Response) (*x509.Certificate, error) {
return nil, errContentType
}
}
-
-func (p *Provider) Register(s Signer, c Contacts) error {
- // first step: new-reg
- req := &Registration{
- Resource: ResNewReg,
- Contact: c,
- }
- resp, err := p.post(p.NewReg, s, req)
- if err != nil {
- return err
- }
- resp.Body.Close()
- ns := parseHeader(resp)
-
- // second step: reg, agree to tos
- req = &Registration{
- Resource: ResReg,
- Agreement: ns.Link["terms-of-service"],
- }
- resp, err = p.post(ns.Location, s, req)
- if err != nil {
- return err
- }
- resp.Body.Close()
- return nil
-}
-
-func (p *Provider) solve(s Signer, ch Challenge, sol Solver) error {
- ka, err := s.KeyAuth(ch.Token)
- if err != nil {
- return err
- }
-
- // prepare solver
- err = sol.Solve(ch.Token, ka)
- if err != nil {
- return err
- }
- defer sol.Solved()
-
- // update challenge
- ch.Resource = ResChallenge
- ch.KeyAuthorization = ka
-
- resp, err := p.post(ch.URI, s, ch)
- if err != nil {
- return err
- }
- ns := parseHeader(resp)
-
- return p.pollStatus(ns.Location)
-}
-
-func (p *Provider) authz(s Signer, domain string, sol map[ChalType]Solver) error {
- // first step: pocke
- req := &Authorization{
- Resource: ResNewAuthz,
- Identifier: Identifier{
- Type: IdentDNS,
- Value: domain,
- },
- }
- resp, err := p.post(p.NewAuthz, s, req)
- if err != nil {
- return err
- }
- err = parseJson(resp, req)
- if err != nil {
- return err
- }
- // second step: choose and start solver
- for _, ch := range req.Supported(sol) {
- if err = p.solve(s, ch, sol[ch.Type]); err != nil {
- return err
- }
- }
- return nil
-}
-
-func (p *Provider) Authorize(s Signer, d *Desire) error {
- for _, domain := range d.altnames {
- if err := p.authz(s, domain, d.solver); err != nil {
- return err
- }
- }
- return nil
-}
-
-func (p *Provider) pollStatus(uri string) error {
- t := time.NewTicker(poll)
- defer t.Stop()
- for range t.C {
- resp, err := p.Get(uri)
- if err != nil {
- return err
- }
- req := new(Challenge)
- err = parseJson(resp, req)
- if err != nil {
- return err
- }
- if req.Err != nil {
- return req.Err
- }
- log.Println("status", req.Status)
- if req.Status == StatusValid {
- return nil
- }
- }
- return nil
-}
-
-func (p *Provider) Bundle(s Signer, d *Desire) error {
- d.cert = make([]*x509.Certificate, 2)
- // first step: post csr
- csr, err := d.CSR()
- if err != nil {
- return err
- }
- req := &CSR{
- Resource: ResNewCert,
- CSR: csr,
- }
- resp, err := p.post(p.NewCert, s, req)
- if err != nil {
- return err
- }
- d.cert[0], err = parseCert(resp)
- if err != nil {
- return err
- }
- ns := parseHeader(resp)
-
- // second step: cet CA
- resp, err = p.Get(ns.Link["up"])
- if err != nil {
- return err
- }
- d.cert[1], err = parseCert(resp)
- if err != nil {
- return err
- }
-
- return nil
-}
diff --git a/register.go b/register.go
new file mode 100644
index 0000000..e002853
--- /dev/null
+++ b/register.go
@@ -0,0 +1,48 @@
+package acme
+
+import (
+ "net"
+ "time"
+
+ "github.com/square/go-jose"
+)
+
+// Registration Objects
+type Registration struct {
+ Resource Resource `json:"resource"` // new-reg
+ Contact Contacts `json:"contact,omitempty"`
+ Agreement string `json:"agreement,omitempty"`
+ Authorizations string `json:"authorizations,omitempty"`
+ Certificates string `json:"certificates,omitempty"`
+ ID int `json:"id,omitempty"`
+ Key *jose.JsonWebKey `json:"key,omitempty"`
+ InitialIP *net.IP `json:"initialIp,omitempty"` // not in draft
+ CreatedAt *time.Time `json:"createdAt,omitempty"`
+}
+
+func (p *Provider) Register(s Signer, c Contacts) error {
+ // first step: new-reg
+ req := &Registration{
+ Resource: ResNewReg,
+ Contact: c,
+ }
+ resp, err := p.post(p.NewReg, s, req)
+ if err != nil {
+ return err
+ }
+ resp.Body.Close()
+ ns := parseHeader(resp)
+
+ // second step: reg, agree to tos
+ req = &Registration{
+ Resource: ResReg,
+ Agreement: ns.Link["terms-of-service"],
+ }
+ resp, err = p.post(ns.Location, s, req)
+ if err != nil {
+ return err
+ }
+ resp.Body.Close()
+
+ return nil
+}