From c6261c5ce9537296479b621e91a90e4f57a21734 Mon Sep 17 00:00:00 2001 From: Dimitri Sokolyuk Date: Sat, 28 Nov 2015 18:52:49 +0100 Subject: WIP error handler --- .gitignore | 1 + account.go | 26 ++++++++++++++++++++++++++ client.go | 40 +++++++++++++++++++++------------------- cmd/acme/main.go | 9 ++++----- errors.go | 25 ++++++++++++++++++++++++- messages.go | 36 ++++++++++++++++++++++++++++-------- nonce.go | 4 ++++ 7 files changed, 108 insertions(+), 33 deletions(-) diff --git a/.gitignore b/.gitignore index 1377554..257fc63 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ *.swp +.acme diff --git a/account.go b/account.go index 62d3213..7766893 100644 --- a/account.go +++ b/account.go @@ -3,12 +3,16 @@ package acme import ( "crypto/rand" "crypto/rsa" + "fmt" "net/mail" + + "github.com/square/go-jose" ) type Account struct { Contact []string `json:"contact"` PrivKey *rsa.PrivateKey `json:"key"` + Signer jose.Signer `json:"-"` } func NewAccount(email string, bits int) (Account, error) { @@ -25,3 +29,25 @@ func NewAccount(email string, bits int) (Account, error) { PrivKey: key, }, nil } + +func (a *Account) Sign(msg []byte) ([]byte, error) { + if a.Signer == nil { + signer, err := jose.NewSigner(jose.RS256, a.PrivKey) + if err != nil { + return nil, err + } + signer.SetNonceSource(nonces) + a.Signer = signer + } + obj, err := a.Signer.Sign(msg) + return []byte(obj.FullSerialize()), err +} + +func (a *Account) ParseSigned(msg []byte) ([]byte, error) { + fmt.Println("MSG", string(msg)) + obj, err := jose.ParseSigned(string(msg)) + if err != nil { + return nil, err + } + return obj.Verify(&a.PrivKey.PublicKey) +} diff --git a/client.go b/client.go index 6e83b5a..06bb907 100644 --- a/client.go +++ b/client.go @@ -3,13 +3,16 @@ package acme import ( "bytes" "encoding/json" - "log" "net/http" "github.com/square/go-jose" ) -var nonces = make(nonce, 100) // buffered channel for nonces +type Signer interface { + Sign([]byte) ([]byte, error) +} + +var nonces = newNonce() func Get(uri string, v interface{}) error { resp, err := http.Get(uri) @@ -21,20 +24,28 @@ func Get(uri string, v interface{}) error { return json.NewDecoder(resp.Body).Decode(v) } -func Post(uri string, v interface{}) error { +func Post(s Signer, uri string, v interface{}) (*http.Response, error) { body, err := json.Marshal(v) if err != nil { - return err + return nil, err } - log.Println(string(body)) - return nil - // premature debug abort - _, err = http.Post(uri, "application/jose+json", bytes.NewReader(body)) + signed, err := s.Sign(body) if err != nil { - return err + return nil, err + } + + resp, err := http.Post(uri, "application/jose+json", bytes.NewReader(signed)) + if err != nil { + return nil, err + } + nonces.parse(resp) + + if resp.StatusCode >= http.StatusBadRequest { + return nil, handleError(resp) } - return nil + + return resp, nil } func Sign(acc Account, body []byte) (string, error) { @@ -49,12 +60,3 @@ func Sign(acc Account, body []byte) (string, error) { } return obj.FullSerialize(), nil } - -func ParseSigned(body string) error { - obj, err := jose.ParseSigned(body) - if err != nil { - return err - } - log.Printf("%+v\n", obj) - return nil -} diff --git a/cmd/acme/main.go b/cmd/acme/main.go index 2e35532..c6250e6 100644 --- a/cmd/acme/main.go +++ b/cmd/acme/main.go @@ -14,7 +14,7 @@ func must(err error) { } const ( - eMail = `Dimitri Sokolyuk ` + eMail = `another@example.com` keySize = 2048 ) @@ -38,8 +38,7 @@ func main() { must(err) acme.SaveKey(".acme/priv.pem", acc.PrivKey) - acme.Post(dir.NewReg, acme.NewRegistration(acc.Contact, acme.NewReg{})) - - //s, _ := acme.Sign(acc, []byte("AAA")) - //acme.ParseSigned(s) + resp, err := acme.Post(&acc, dir.NewReg, acme.NewRegistration(acc.Contact, acme.NewReg{})) + must(err) + log.Println(resp) } diff --git a/errors.go b/errors.go index f23aae3..a321863 100644 --- a/errors.go +++ b/errors.go @@ -1,6 +1,12 @@ package acme -import "errors" +import ( + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "net/http" +) var ( errBadCSR = errors.New("The CSR is unacceptable (e.g., due to a short key)") @@ -26,3 +32,20 @@ var Errors = map[string]error{ "urn:acme:error:unauthorized": errUnauthorized, "urn:acme:error:unknownHost": errUnknownHost, } + +func handleError(r *http.Response) error { + defer r.Body.Close() + body, err := ioutil.ReadAll(r.Body) + if err != nil { + return err + } + var p Problem + err = json.Unmarshal(body, &p) + if err != nil { + return err + } + if e, ok := Errors[p.Type]; ok { + return fmt.Errorf("%v: %v", e, p.Detail) + } + return errors.New(p.Detail) +} diff --git a/messages.go b/messages.go index 07c6fe7..511c713 100644 --- a/messages.go +++ b/messages.go @@ -1,7 +1,5 @@ package acme -import jose "github.com/square/go-jose" - const ( // LEV1 Let's Encrytpt V1 LEV1 = `https://acme-v01.api.letsencrypt.org/directory` @@ -20,12 +18,27 @@ type Directory struct { // Registration Objects type Registration struct { - Resource string `json:"resource"` - Key jose.JsonWebKey `json:"key"` - Contact []string `json:"contact,omitempty"` - Agreement string `json:"agreement,omitempty"` - Authorizations string `json:"authorizations,omitempty"` - Certificates string `json:"certificates,omitempty"` + Resource string `json:"resource"` + Contact []string `json:"contact,omitempty"` + Agreement string `json:"agreement,omitempty"` + Authorizations string `json:"authorizations,omitempty"` + Certificates string `json:"certificates,omitempty"` +} + +// RegistrationResp ... +type RegistrationResp struct { + ID int `json:"id"` + Key Key `json:"key"` + Contact []string `json:"contact"` + InitialIP string `json:"initalIp"` + CreatedAt string `json:"createdAt"` // 2006-01-02T15:04:05.999999999Z +} + +// Key ... +type Key struct { + Kty string `json:"kty"` // RSA, EC + E string `json:"e"` + N string `json:"n"` } // Authorization Objects @@ -51,3 +64,10 @@ type Challege struct { Validated string `json:"validated"` // 2006-01-02T15:04Z KeyAuthorization string `json:"keyAuthorization"` } + +// Problem description +type Problem struct { + Type string `json:"type"` + Detail string `json:"detail"` + Instance string `json:"instance"` +} diff --git a/nonce.go b/nonce.go index 95440ab..88e67ca 100644 --- a/nonce.go +++ b/nonce.go @@ -9,6 +9,10 @@ var errNoNonces = errors.New("No nonces available") type nonce chan string +func newNonce() nonce { + return make(nonce, 100) +} + func (n nonce) parse(r *http.Response) { if nonce := r.Header.Get("Replay-Nonce"); nonce != "" { n <- nonce -- cgit v1.2.3