<commit_before>// Copyright 2014 ISRG.  All rights reserved
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package wfe

import (
	"bytes"
	"crypto/x509"
	"database/sql"
	"encoding/json"
	"errors"
	"fmt"
	"html/template"
	"io/ioutil"
	"net/http"
	"net/url"
	"regexp"
	"strconv"
	"strings"

	"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cactus/go-statsd-client/statsd"
	jose "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/square/go-jose"
	"github.com/letsencrypt/boulder/core"
	blog "github.com/letsencrypt/boulder/log"
)

const (
	NewRegPath     = "/acme/new-reg"
	RegPath        = "/acme/reg/"
	NewAuthzPath   = "/acme/new-authz"
	AuthzPath      = "/acme/authz/"
	NewCertPath    = "/acme/new-cert"
	CertPath       = "/acme/cert/"
	RevokeCertPath = "/acme/revoke-cert/"
	TermsPath      = "/terms"
	IssuerPath     = "/acme/issuer-cert"
	BuildIDPath    = "/build"
)

// WebFrontEndImpl represents a Boulder web service and its resources
type WebFrontEndImpl struct {
	RA    core.RegistrationAuthority
	SA    core.StorageGetter
	Stats statsd.Statter
	log   *blog.AuditLogger

	// URL configuration parameters
	BaseURL   string
	NewReg    string
	RegBase   string
	NewAuthz  string
	AuthzBase string
	NewCert   string
	CertBase  string

	// Issuer certificate (DER) for /acme/issuer-cert
	IssuerCert []byte

	// URL to the current subscriber agreement (should contain some version identifier)
	SubscriberAgreementURL string
}

// NewWebFrontEndImpl constructs a web service for Boulder
func NewWebFrontEndImpl() WebFrontEndImpl {
	logger := blog.GetAuditLogger()
	logger.Notice("Web Front End Starting")
	return WebFrontEndImpl{
		log: logger,
	}
}

func (wfe *WebFrontEndImpl) HandlePaths() {
	wfe.NewReg = wfe.BaseURL + NewRegPath
	wfe.RegBase = wfe.BaseURL + RegPath
	wfe.NewAuthz = wfe.BaseURL + NewAuthzPath
	wfe.AuthzBase = wfe.BaseURL + AuthzPath
	wfe.NewCert = wfe.BaseURL + NewCertPath
	wfe.CertBase = wfe.BaseURL + CertPath

	http.HandleFunc("/", wfe.Index)
	http.HandleFunc(NewRegPath, wfe.NewRegistration)
	http.HandleFunc(NewAuthzPath, wfe.NewAuthorization)
	http.HandleFunc(NewCertPath, wfe.NewCertificate)
	http.HandleFunc(RegPath, wfe.Registration)
	http.HandleFunc(AuthzPath, wfe.Authorization)
	http.HandleFunc(CertPath, wfe.Certificate)
	http.HandleFunc(RevokeCertPath, wfe.RevokeCertificate)
	http.HandleFunc(TermsPath, wfe.Terms)
	http.HandleFunc(IssuerPath, wfe.Issuer)
	http.HandleFunc(BuildIDPath, wfe.BuildID)
}

// Method implementations

func (wfe *WebFrontEndImpl) Index(response http.ResponseWriter, request *http.Request) {
	// http://golang.org/pkg/net/http/#example_ServeMux_Handle
	// The "/" pattern matches everything, so we need to check
	// that we're at the root here.
	if request.URL.Path != "/" {
		http.NotFound(response, request)
		return
	}

	tmpl := template.Must(template.New("body").Parse(`<html>
  <body>
    <a href="https://letsencrypt.org/">Let's Encrypt</a> Certificate Authority
    running <a href="https://github.com/letsencrypt/boulder">Boulder</a>,
    a reference <a href="https://letsencrypt.github.io/acme-spec/">ACME</a>
    server implementation. New registration is available at
    <a href="{{.NewReg}}">{{.NewReg}}</a>.
  </body>
</html>
`))
	tmpl.Execute(response, wfe)
	response.Header().Set("Content-Type", "text/html")
}

// The ID is always the last slash-separated token in the path
func parseIDFromPath(path string) string {
	re := regexp.MustCompile("^.*/")
	return re.ReplaceAllString(path, "")
}

// ProblemType objects represent problem documents, which are
// returned with HTTP error responses
// https://tools.ietf.org/html/draft-ietf-appsawg-http-problem-00
type ProblemType string

type problem struct {
	Type   ProblemType `json:"type,omitempty"`
	Detail string      `json:"detail,omitempty"`
}

const (
	MalformedProblem      = ProblemType("urn:acme:error:malformed")
	UnauthorizedProblem   = ProblemType("urn:acme:error:unauthorized")
	ServerInternalProblem = ProblemType("urn:acme:error:serverInternal")
)

func (wfe *WebFrontEndImpl) verifyPOST(request *http.Request, regCheck bool) ([]byte, *jose.JsonWebKey, core.Registration, error) {
	var reg core.Registration

	// Read body
	if request.Body == nil {
		return nil, nil, reg, errors.New("No body on POST")
	}

	body, err := ioutil.ReadAll(request.Body)
	if err != nil {
		return nil, nil, reg, err
	}

	// Parse as JWS
	parsedJws, err := jose.ParseSigned(string(body))
	if err != nil {
		wfe.log.Debug(fmt.Sprintf("Parse error reading JWS: %v", err))
		return nil, nil, reg, err
	}

	// Verify JWS
	// NOTE: It might seem insecure for the WFE to be trusted to verify
	// client requests, i.e., that the verification should be done at the
	// RA.  However the WFE is the RA's only view of the outside world
	// *anyway*, so it could always lie about what key was used by faking
	// the signature itself.
	if len(parsedJws.Signatures) > 1 {
		wfe.log.Debug(fmt.Sprintf("Too many signatures on POST"))
		return nil, nil, reg, errors.New("Too many signatures on POST")
	}
	if len(parsedJws.Signatures) == 0 {
		wfe.log.Debug(fmt.Sprintf("POST not signed: %v", parsedJws))
		return nil, nil, reg, errors.New("POST not signed")
	}
	key := parsedJws.Signatures[0].Header.JsonWebKey
	payload, err := parsedJws.Verify(key)
	if err != nil {
		wfe.log.Debug(string(body))
		wfe.log.Debug(fmt.Sprintf("JWS verification error: %v", err))
		return nil, nil, reg, err
	}

	if regCheck {
		// Check that the key is assosiated with an actual account
		reg, err = wfe.SA.GetRegistrationByKey(*key)
		if err != nil {
			return nil, nil, reg, err
		}
	}

	return []byte(payload), key, reg, nil
}

// Notify the client of an error condition and log it for audit purposes.
func (wfe *WebFrontEndImpl) sendError(response http.ResponseWriter, details string, debug interface{}, code int) {
	problem := problem{Detail: details}
	switch code {
	case http.StatusForbidden:
		problem.Type = UnauthorizedProblem
	case http.StatusMethodNotAllowed:
		fallthrough
	case http.StatusNotFound:
		fallthrough
	case http.StatusBadRequest:
		problem.Type = MalformedProblem
	case http.StatusInternalServerError:
		problem.Type = ServerInternalProblem
	}

	problemDoc, err := json.Marshal(problem)
	if err != nil {
		// AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3
		wfe.log.Audit(fmt.Sprintf("Could not marshal error message: %s - %+v", err.Error(), problem))
		problemDoc = []byte("{\"detail\": \"Problem marshalling error message.\"}")
	}

	switch problem.Type {
	case ServerInternalProblem:
		// AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3
		wfe.log.Audit(fmt.Sprintf("Internal error - %s - %s", details, debug))
	case MalformedProblem:
		// AUDIT[ Improper Messages ] 0786b6f2-91ca-4f48-9883-842a19084c64
		wfe.log.Audit(fmt.Sprintf("Improper HTTP request - %s - %s", details, debug))
	case UnauthorizedProblem:
		// AUDIT[ Improper Messages ] 0786b6f2-91ca-4f48-9883-842a19084c64
		wfe.log.Audit(fmt.Sprintf("Unauthorized HTTP request - %s - %s", details, debug))
	}

	// Paraphrased from
	// https://golang.org/src/net/http/server.go#L1272
	response.Header().Set("Content-Type", "application/problem+json")
	response.WriteHeader(code)
	response.Write(problemDoc)
}

func link(url, relation string) string {
	return fmt.Sprintf("<%s>;rel=\"%s\"", url, relation)
}

func (wfe *WebFrontEndImpl) NewRegistration(response http.ResponseWriter, request *http.Request) {
	if request.Method != "POST" {
		wfe.sendError(response, "Method not allowed", "", http.StatusMethodNotAllowed)
		return
	}

	body, key, _, err := wfe.verifyPOST(request, false)
	if err != nil {
		wfe.sendError(response, "Unable to read/verify body", err, http.StatusBadRequest)
		return
	}

	var init core.Registration
	err = json.Unmarshal(body, &init)
	if err != nil {
		wfe.sendError(response, "Error unmarshaling JSON", err, http.StatusBadRequest)
		return
	}
	if len(init.Agreement) > 0 && init.Agreement != wfe.SubscriberAgreementURL {
		wfe.sendError(response, fmt.Sprintf("Provided agreement URL [%s] does not match current agreement URL [%s]", init.Agreement, wfe.SubscriberAgreementURL), nil, http.StatusBadRequest)
		return
	}
	init.Key = *key

	reg, err := wfe.RA.NewRegistration(init)
	if err != nil {
		wfe.sendError(response, "Error creating new registration", err, http.StatusInternalServerError)
		return
	}

	// Use an explicitly typed variable. Otherwise `go vet' incorrectly complains
	// that reg.ID is a string being passed to %d.
	var id int64 = reg.ID
	regURL := fmt.Sprintf("%s%d", wfe.RegBase, id)
	responseBody, err := json.Marshal(reg)
	if err != nil {
		wfe.sendError(response, "Error marshaling registration", err, http.StatusInternalServerError)
		return
	}

	response.Header().Add("Location", regURL)
	response.Header().Set("Content-Type", "application/json")
	response.Header().Add("Link", link(wfe.NewAuthz, "next"))
	if len(wfe.SubscriberAgreementURL) > 0 {
		response.Header().Add("Link", link(wfe.SubscriberAgreementURL, "terms-of-service"))
	}

	response.WriteHeader(http.StatusCreated)
	response.Write(responseBody)

	// incr reg stat
	wfe.Stats.Inc("Registrations", 1, 1.0)
}

func (wfe *WebFrontEndImpl) NewAuthorization(response http.ResponseWriter, request *http.Request) {
	if request.Method != "POST" {
		wfe.sendError(response, "Method not allowed", request.Method, http.StatusMethodNotAllowed)
		return
	}

	body, _, currReg, err := wfe.verifyPOST(request, true)
	if err != nil {
		if err == sql.ErrNoRows {
			wfe.sendError(response, "No registration exists matching provided key", err, http.StatusForbidden)
		} else {
			wfe.sendError(response, "Unable to read/verify body", err, http.StatusBadRequest)
		}
		return
	}
	if currReg.Agreement != wfe.SubscriberAgreementURL {
		wfe.sendError(response, "Must agree to subscriber agreement before any further actions", nil, http.StatusForbidden)
		return
	}

	var init core.Authorization
	if err = json.Unmarshal(body, &init); err != nil {
		wfe.sendError(response, "Error unmarshaling JSON", err, http.StatusBadRequest)
		return
	}

	// Create new authz and return
	authz, err := wfe.RA.NewAuthorization(init, currReg.ID)
	if err != nil {
		wfe.sendError(response,
			"Error creating new authz", err,
			http.StatusInternalServerError)
		return
	}

	// Make a URL for this authz, then blow away the ID and RegID before serializing
	authzURL := wfe.AuthzBase + string(authz.ID)
	authz.ID = ""
	authz.RegistrationID = 0
	responseBody, err := json.Marshal(authz)
	if err != nil {
		wfe.sendError(response, "Error marshaling authz", err, http.StatusInternalServerError)
		return
	}

	response.Header().Add("Location", authzURL)
	response.Header().Add("Link", link(wfe.NewCert, "next"))
	response.Header().Set("Content-Type", "application/json")
	response.WriteHeader(http.StatusCreated)
	if _, err = response.Write(responseBody); err != nil {
		wfe.log.Warning(fmt.Sprintf("Could not write response: %s", err))
	}
	// incr pending auth stat (?)
	wfe.Stats.Inc("PendingAuthorizations", 1, 1.0)
}

func (wfe *WebFrontEndImpl) RevokeCertificate(response http.ResponseWriter, request *http.Request) {
	if request.Method != "POST" {
		wfe.sendError(response, "Method not allowed", request.Method, http.StatusMethodNotAllowed)
		return
	}

	// We don't ask verifyPOST to verify there is a correponding registration,
	// because anyone with the right private key can revoke a certificate.
	body, requestKey, _, err := wfe.verifyPOST(request, false)
	if err != nil {
		wfe.sendError(response, "Unable to read/verify body", err, http.StatusBadRequest)
		return
	}

	type RevokeRequest struct {
		CertificateDER core.JsonBuffer `json:"certificate"`
	}
	var revokeRequest RevokeRequest
	if err = json.Unmarshal(body, &revokeRequest); err != nil {
		wfe.log.Debug(fmt.Sprintf("Couldn't unmarshal in revoke request %s", string(body)))
		wfe.sendError(response, "Unable to read/verify body", err, http.StatusBadRequest)
		return
	}
	providedCert, err := x509.ParseCertificate(revokeRequest.CertificateDER)
	if err != nil {
		wfe.log.Debug("Couldn't parse cert in revoke request.")
		wfe.sendError(response, "Unable to read/verify body", err, http.StatusBadRequest)
		return
	}

	serial := core.SerialToString(providedCert.SerialNumber)
	certDER, err := wfe.SA.GetCertificate(serial)
	if err != nil || !bytes.Equal(certDER, revokeRequest.CertificateDER) {
		wfe.sendError(response, "No such certificate", err, http.StatusNotFound)
		return
	}
	parsedCertificate, err := x509.ParseCertificate(certDER)
	if err != nil {
		wfe.sendError(response, "Invalid certificate", err, http.StatusInternalServerError)
		return
	}

	certStatus, err := wfe.SA.GetCertificateStatus(serial)
	if err != nil {
		wfe.sendError(response, "No such certificate", err, http.StatusNotFound)
		return
	}

	if certStatus.Status == core.OCSPStatusRevoked {
		wfe.sendError(response, "Certificate already revoked", "", http.StatusConflict)
		return
	}

	// TODO: Implement other methods of validating revocation, e.g. through
	// authorizations on account.
	if !core.KeyDigestEquals(requestKey, parsedCertificate.PublicKey) {
		wfe.log.Debug("Key mismatch for revoke")
		wfe.sendError(response,
			"Revocation request must be signed by private key of cert to be revoked",
			requestKey,
			http.StatusForbidden)
		return
	}

	err = wfe.RA.RevokeCertificate(*parsedCertificate)
	if err != nil {
		wfe.sendError(response,
			"Failed to revoke certificate",
			err,
			http.StatusInternalServerError)
	} else {
		wfe.log.Debug(fmt.Sprintf("Revoked %v", serial))
		// incr revoked cert stat
		wfe.Stats.Inc("RevokedCertificates", 1, 1.0)
		response.WriteHeader(http.StatusOK)
	}
}

func (wfe *WebFrontEndImpl) NewCertificate(response http.ResponseWriter, request *http.Request) {
	if request.Method != "POST" {
		wfe.sendError(response, "Method not allowed", request.Method, http.StatusMethodNotAllowed)
		return
	}

	body, key, reg, err := wfe.verifyPOST(request, true)
	if err != nil {
		if err == sql.ErrNoRows {
			wfe.sendError(response, "No registration exists matching provided key", err, http.StatusForbidden)
		} else {
			wfe.sendError(response, "Unable to read/verify body", err, http.StatusBadRequest)
		}
		return
	}
	if reg.Agreement != wfe.SubscriberAgreementURL {
		wfe.sendError(response, "Must agree to subscriber agreement before any further actions", nil, http.StatusForbidden)
		return
	}

	var init core.CertificateRequest
	if err = json.Unmarshal(body, &init); err != nil {
		fmt.Println(err)
		wfe.sendError(response, "Error unmarshaling certificate request", err, http.StatusBadRequest)
		return
	}

	wfe.log.Notice(fmt.Sprintf("Client requested new certificate: %v %v %v",
		request.RemoteAddr, init, key))

	// Create new certificate and return
	// TODO IMPORTANT: The RA trusts the WFE to provide the correct key. If the
	// WFE is compromised, *and* the attacker knows the public key of an account
	// authorized for target site, they could cause issuance for that site by
	// lying to the RA. We should probably pass a copy of the whole rquest to the
	// RA for secondary validation.
	cert, err := wfe.RA.NewCertificate(init, reg.ID)
	if err != nil {
		wfe.sendError(response,
			"Error creating new cert", err,
			http.StatusBadRequest)
		return
	}

	// Make a URL for this certificate.
	// We use only the sequential part of the serial number, because it should
	// uniquely identify the certificate, and this makes it easy for anybody to
	// enumerate and mirror our certificates.
	parsedCertificate, err := x509.ParseCertificate([]byte(cert.DER))
	if err != nil {
		wfe.sendError(response,
			"Error creating new cert", err,
			http.StatusBadRequest)
		return
	}
	serial := parsedCertificate.SerialNumber
	certURL := fmt.Sprintf("%s%016x", wfe.CertBase, serial.Rsh(serial, 64))

	// TODO Content negotiation
	response.Header().Add("Location", certURL)
	response.Header().Add("Link", link(wfe.BaseURL+IssuerPath, "up"))
	response.Header().Set("Content-Type", "application/pkix-cert")
	response.WriteHeader(http.StatusCreated)
	if _, err = response.Write(cert.DER); err != nil {
		wfe.log.Warning(fmt.Sprintf("Could not write response: %s", err))
	}
	// incr cert stat
	wfe.Stats.Inc("Certificates", 1, 1.0)
}

func (wfe *WebFrontEndImpl) Challenge(authz core.Authorization, response http.ResponseWriter, request *http.Request) {
	// Check that the requested challenge exists within the authorization
	found := false
	var challengeIndex int
	for i, challenge := range authz.Challenges {
		tempURL := url.URL(challenge.URI)
		if tempURL.Path == request.URL.Path && tempURL.RawQuery == request.URL.RawQuery {
			found = true
			challengeIndex = i
			break
		}
	}

	if !found {
		wfe.sendError(response, "Unable to find challenge", request.URL.RawQuery, http.StatusNotFound)
		return
	}

	switch request.Method {
	default:
		wfe.sendError(response, "Method not allowed", "", http.StatusMethodNotAllowed)
		return

	case "POST":
		body, _, currReg, err := wfe.verifyPOST(request, true)
		if err != nil {
			if err == sql.ErrNoRows {
				wfe.sendError(response, "No registration exists matching provided key", err, http.StatusForbidden)
			} else {
				wfe.sendError(response, "Unable to read/verify body", err, http.StatusBadRequest)
			}
			return
		}
		if currReg.Agreement != wfe.SubscriberAgreementURL {
			wfe.sendError(response, "Must agree to subscriber agreement before any further actions", nil, http.StatusForbidden)
			return
		}

		var challengeResponse core.Challenge
		if err = json.Unmarshal(body, &challengeResponse); err != nil {
			wfe.sendError(response, "Error unmarshaling challenge response", err, http.StatusBadRequest)
			return
		}

		// Check that the registration ID matching the key used matches
		// the registration ID on the authz object
		if currReg.ID != authz.RegistrationID {
			wfe.sendError(response, "User registration ID doesn't match registration ID in authorization",
				fmt.Sprintf("User: %v != Authorization: %v", currReg.ID, authz.RegistrationID),
				http.StatusForbidden)
			return
		}

		// Ask the RA to update this authorization
		updatedAuthz, err := wfe.RA.UpdateAuthorization(authz, challengeIndex, challengeResponse)
		if err != nil {
			wfe.sendError(response, "Unable to update authorization", err, http.StatusInternalServerError)
			return<commit_msg>Do registration check before attempting to parse response<commit_after>// Copyright 2014 ISRG.  All rights reserved
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package wfe

import (
	"bytes"
	"crypto/x509"
	"database/sql"
	"encoding/json"
	"errors"
	"fmt"
	"html/template"
	"io/ioutil"
	"net/http"
	"net/url"
	"regexp"
	"strconv"
	"strings"

	"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cactus/go-statsd-client/statsd"
	jose "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/square/go-jose"
	"github.com/letsencrypt/boulder/core"
	blog "github.com/letsencrypt/boulder/log"
)

const (
	NewRegPath     = "/acme/new-reg"
	RegPath        = "/acme/reg/"
	NewAuthzPath   = "/acme/new-authz"
	AuthzPath      = "/acme/authz/"
	NewCertPath    = "/acme/new-cert"
	CertPath       = "/acme/cert/"
	RevokeCertPath = "/acme/revoke-cert/"
	TermsPath      = "/terms"
	IssuerPath     = "/acme/issuer-cert"
	BuildIDPath    = "/build"
)

// WebFrontEndImpl represents a Boulder web service and its resources
type WebFrontEndImpl struct {
	RA    core.RegistrationAuthority
	SA    core.StorageGetter
	Stats statsd.Statter
	log   *blog.AuditLogger

	// URL configuration parameters
	BaseURL   string
	NewReg    string
	RegBase   string
	NewAuthz  string
	AuthzBase string
	NewCert   string
	CertBase  string

	// Issuer certificate (DER) for /acme/issuer-cert
	IssuerCert []byte

	// URL to the current subscriber agreement (should contain some version identifier)
	SubscriberAgreementURL string
}

// NewWebFrontEndImpl constructs a web service for Boulder
func NewWebFrontEndImpl() WebFrontEndImpl {
	logger := blog.GetAuditLogger()
	logger.Notice("Web Front End Starting")
	return WebFrontEndImpl{
		log: logger,
	}
}

func (wfe *WebFrontEndImpl) HandlePaths() {
	wfe.NewReg = wfe.BaseURL + NewRegPath
	wfe.RegBase = wfe.BaseURL + RegPath
	wfe.NewAuthz = wfe.BaseURL + NewAuthzPath
	wfe.AuthzBase = wfe.BaseURL + AuthzPath
	wfe.NewCert = wfe.BaseURL + NewCertPath
	wfe.CertBase = wfe.BaseURL + CertPath

	http.HandleFunc("/", wfe.Index)
	http.HandleFunc(NewRegPath, wfe.NewRegistration)
	http.HandleFunc(NewAuthzPath, wfe.NewAuthorization)
	http.HandleFunc(NewCertPath, wfe.NewCertificate)
	http.HandleFunc(RegPath, wfe.Registration)
	http.HandleFunc(AuthzPath, wfe.Authorization)
	http.HandleFunc(CertPath, wfe.Certificate)
	http.HandleFunc(RevokeCertPath, wfe.RevokeCertificate)
	http.HandleFunc(TermsPath, wfe.Terms)
	http.HandleFunc(IssuerPath, wfe.Issuer)
	http.HandleFunc(BuildIDPath, wfe.BuildID)
}

// Method implementations

func (wfe *WebFrontEndImpl) Index(response http.ResponseWriter, request *http.Request) {
	// http://golang.org/pkg/net/http/#example_ServeMux_Handle
	// The "/" pattern matches everything, so we need to check
	// that we're at the root here.
	if request.URL.Path != "/" {
		http.NotFound(response, request)
		return
	}

	tmpl := template.Must(template.New("body").Parse(`<html>
  <body>
    <a href="https://letsencrypt.org/">Let's Encrypt</a> Certificate Authority
    running <a href="https://github.com/letsencrypt/boulder">Boulder</a>,
    a reference <a href="https://letsencrypt.github.io/acme-spec/">ACME</a>
    server implementation. New registration is available at
    <a href="{{.NewReg}}">{{.NewReg}}</a>.
  </body>
</html>
`))
	tmpl.Execute(response, wfe)
	response.Header().Set("Content-Type", "text/html")
}

// The ID is always the last slash-separated token in the path
func parseIDFromPath(path string) string {
	re := regexp.MustCompile("^.*/")
	return re.ReplaceAllString(path, "")
}

// ProblemType objects represent problem documents, which are
// returned with HTTP error responses
// https://tools.ietf.org/html/draft-ietf-appsawg-http-problem-00
type ProblemType string

type problem struct {
	Type   ProblemType `json:"type,omitempty"`
	Detail string      `json:"detail,omitempty"`
}

const (
	MalformedProblem      = ProblemType("urn:acme:error:malformed")
	UnauthorizedProblem   = ProblemType("urn:acme:error:unauthorized")
	ServerInternalProblem = ProblemType("urn:acme:error:serverInternal")
)

func (wfe *WebFrontEndImpl) verifyPOST(request *http.Request, regCheck bool) ([]byte, *jose.JsonWebKey, core.Registration, error) {
	var reg core.Registration

	// Read body
	if request.Body == nil {
		return nil, nil, reg, errors.New("No body on POST")
	}

	body, err := ioutil.ReadAll(request.Body)
	if err != nil {
		return nil, nil, reg, err
	}

	// Parse as JWS
	parsedJws, err := jose.ParseSigned(string(body))
	if err != nil {
		wfe.log.Debug(fmt.Sprintf("Parse error reading JWS: %v", err))
		return nil, nil, reg, err
	}

	// Verify JWS
	// NOTE: It might seem insecure for the WFE to be trusted to verify
	// client requests, i.e., that the verification should be done at the
	// RA.  However the WFE is the RA's only view of the outside world
	// *anyway*, so it could always lie about what key was used by faking
	// the signature itself.
	if len(parsedJws.Signatures) > 1 {
		wfe.log.Debug(fmt.Sprintf("Too many signatures on POST"))
		return nil, nil, reg, errors.New("Too many signatures on POST")
	}
	if len(parsedJws.Signatures) == 0 {
		wfe.log.Debug(fmt.Sprintf("POST not signed: %v", parsedJws))
		return nil, nil, reg, errors.New("POST not signed")
	}
	key := parsedJws.Signatures[0].Header.JsonWebKey
	payload, err := parsedJws.Verify(key)
	if err != nil {
		wfe.log.Debug(string(body))
		wfe.log.Debug(fmt.Sprintf("JWS verification error: %v", err))
		return nil, nil, reg, err
	}

	if regCheck {
		// Check that the key is assosiated with an actual account
		reg, err = wfe.SA.GetRegistrationByKey(*key)
		if err != nil {
			return nil, nil, reg, err
		}
	}

	return []byte(payload), key, reg, nil
}

// Notify the client of an error condition and log it for audit purposes.
func (wfe *WebFrontEndImpl) sendError(response http.ResponseWriter, details string, debug interface{}, code int) {
	problem := problem{Detail: details}
	switch code {
	case http.StatusForbidden:
		problem.Type = UnauthorizedProblem
	case http.StatusMethodNotAllowed:
		fallthrough
	case http.StatusNotFound:
		fallthrough
	case http.StatusBadRequest:
		problem.Type = MalformedProblem
	case http.StatusInternalServerError:
		problem.Type = ServerInternalProblem
	}

	problemDoc, err := json.Marshal(problem)
	if err != nil {
		// AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3
		wfe.log.Audit(fmt.Sprintf("Could not marshal error message: %s - %+v", err.Error(), problem))
		problemDoc = []byte("{\"detail\": \"Problem marshalling error message.\"}")
	}

	switch problem.Type {
	case ServerInternalProblem:
		// AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3
		wfe.log.Audit(fmt.Sprintf("Internal error - %s - %s", details, debug))
	case MalformedProblem:
		// AUDIT[ Improper Messages ] 0786b6f2-91ca-4f48-9883-842a19084c64
		wfe.log.Audit(fmt.Sprintf("Improper HTTP request - %s - %s", details, debug))
	case UnauthorizedProblem:
		// AUDIT[ Improper Messages ] 0786b6f2-91ca-4f48-9883-842a19084c64
		wfe.log.Audit(fmt.Sprintf("Unauthorized HTTP request - %s - %s", details, debug))
	}

	// Paraphrased from
	// https://golang.org/src/net/http/server.go#L1272
	response.Header().Set("Content-Type", "application/problem+json")
	response.WriteHeader(code)
	response.Write(problemDoc)
}

func link(url, relation string) string {
	return fmt.Sprintf("<%s>;rel=\"%s\"", url, relation)
}

func (wfe *WebFrontEndImpl) NewRegistration(response http.ResponseWriter, request *http.Request) {
	if request.Method != "POST" {
		wfe.sendError(response, "Method not allowed", "", http.StatusMethodNotAllowed)
		return
	}

	body, key, _, err := wfe.verifyPOST(request, false)
	if err != nil {
		wfe.sendError(response, "Unable to read/verify body", err, http.StatusBadRequest)
		return
	}

	var init core.Registration
	err = json.Unmarshal(body, &init)
	if err != nil {
		wfe.sendError(response, "Error unmarshaling JSON", err, http.StatusBadRequest)
		return
	}
	if len(init.Agreement) > 0 && init.Agreement != wfe.SubscriberAgreementURL {
		wfe.sendError(response, fmt.Sprintf("Provided agreement URL [%s] does not match current agreement URL [%s]", init.Agreement, wfe.SubscriberAgreementURL), nil, http.StatusBadRequest)
		return
	}
	init.Key = *key

	reg, err := wfe.RA.NewRegistration(init)
	if err != nil {
		wfe.sendError(response, "Error creating new registration", err, http.StatusInternalServerError)
		return
	}

	// Use an explicitly typed variable. Otherwise `go vet' incorrectly complains
	// that reg.ID is a string being passed to %d.
	var id int64 = reg.ID
	regURL := fmt.Sprintf("%s%d", wfe.RegBase, id)
	responseBody, err := json.Marshal(reg)
	if err != nil {
		wfe.sendError(response, "Error marshaling registration", err, http.StatusInternalServerError)
		return
	}

	response.Header().Add("Location", regURL)
	response.Header().Set("Content-Type", "application/json")
	response.Header().Add("Link", link(wfe.NewAuthz, "next"))
	if len(wfe.SubscriberAgreementURL) > 0 {
		response.Header().Add("Link", link(wfe.SubscriberAgreementURL, "terms-of-service"))
	}

	response.WriteHeader(http.StatusCreated)
	response.Write(responseBody)

	// incr reg stat
	wfe.Stats.Inc("Registrations", 1, 1.0)
}

func (wfe *WebFrontEndImpl) NewAuthorization(response http.ResponseWriter, request *http.Request) {
	if request.Method != "POST" {
		wfe.sendError(response, "Method not allowed", request.Method, http.StatusMethodNotAllowed)
		return
	}

	body, _, currReg, err := wfe.verifyPOST(request, true)
	if err != nil {
		if err == sql.ErrNoRows {
			wfe.sendError(response, "No registration exists matching provided key", err, http.StatusForbidden)
		} else {
			wfe.sendError(response, "Unable to read/verify body", err, http.StatusBadRequest)
		}
		return
	}
	if currReg.Agreement != wfe.SubscriberAgreementURL {
		wfe.sendError(response, "Must agree to subscriber agreement before any further actions", nil, http.StatusForbidden)
		return
	}

	var init core.Authorization
	if err = json.Unmarshal(body, &init); err != nil {
		wfe.sendError(response, "Error unmarshaling JSON", err, http.StatusBadRequest)
		return
	}

	// Create new authz and return
	authz, err := wfe.RA.NewAuthorization(init, currReg.ID)
	if err != nil {
		wfe.sendError(response,
			"Error creating new authz", err,
			http.StatusInternalServerError)
		return
	}

	// Make a URL for this authz, then blow away the ID and RegID before serializing
	authzURL := wfe.AuthzBase + string(authz.ID)
	authz.ID = ""
	authz.RegistrationID = 0
	responseBody, err := json.Marshal(authz)
	if err != nil {
		wfe.sendError(response, "Error marshaling authz", err, http.StatusInternalServerError)
		return
	}

	response.Header().Add("Location", authzURL)
	response.Header().Add("Link", link(wfe.NewCert, "next"))
	response.Header().Set("Content-Type", "application/json")
	response.WriteHeader(http.StatusCreated)
	if _, err = response.Write(responseBody); err != nil {
		wfe.log.Warning(fmt.Sprintf("Could not write response: %s", err))
	}
	// incr pending auth stat (?)
	wfe.Stats.Inc("PendingAuthorizations", 1, 1.0)
}

func (wfe *WebFrontEndImpl) RevokeCertificate(response http.ResponseWriter, request *http.Request) {
	if request.Method != "POST" {
		wfe.sendError(response, "Method not allowed", request.Method, http.StatusMethodNotAllowed)
		return
	}

	// We don't ask verifyPOST to verify there is a correponding registration,
	// because anyone with the right private key can revoke a certificate.
	body, requestKey, _, err := wfe.verifyPOST(request, false)
	if err != nil {
		wfe.sendError(response, "Unable to read/verify body", err, http.StatusBadRequest)
		return
	}

	type RevokeRequest struct {
		CertificateDER core.JsonBuffer `json:"certificate"`
	}
	var revokeRequest RevokeRequest
	if err = json.Unmarshal(body, &revokeRequest); err != nil {
		wfe.log.Debug(fmt.Sprintf("Couldn't unmarshal in revoke request %s", string(body)))
		wfe.sendError(response, "Unable to read/verify body", err, http.StatusBadRequest)
		return
	}
	providedCert, err := x509.ParseCertificate(revokeRequest.CertificateDER)
	if err != nil {
		wfe.log.Debug("Couldn't parse cert in revoke request.")
		wfe.sendError(response, "Unable to read/verify body", err, http.StatusBadRequest)
		return
	}

	serial := core.SerialToString(providedCert.SerialNumber)
	certDER, err := wfe.SA.GetCertificate(serial)
	if err != nil || !bytes.Equal(certDER, revokeRequest.CertificateDER) {
		wfe.sendError(response, "No such certificate", err, http.StatusNotFound)
		return
	}
	parsedCertificate, err := x509.ParseCertificate(certDER)
	if err != nil {
		wfe.sendError(response, "Invalid certificate", err, http.StatusInternalServerError)
		return
	}

	certStatus, err := wfe.SA.GetCertificateStatus(serial)
	if err != nil {
		wfe.sendError(response, "No such certificate", err, http.StatusNotFound)
		return
	}

	if certStatus.Status == core.OCSPStatusRevoked {
		wfe.sendError(response, "Certificate already revoked", "", http.StatusConflict)
		return
	}

	// TODO: Implement other methods of validating revocation, e.g. through
	// authorizations on account.
	if !core.KeyDigestEquals(requestKey, parsedCertificate.PublicKey) {
		wfe.log.Debug("Key mismatch for revoke")
		wfe.sendError(response,
			"Revocation request must be signed by private key of cert to be revoked",
			requestKey,
			http.StatusForbidden)
		return
	}

	err = wfe.RA.RevokeCertificate(*parsedCertificate)
	if err != nil {
		wfe.sendError(response,
			"Failed to revoke certificate",
			err,
			http.StatusInternalServerError)
	} else {
		wfe.log.Debug(fmt.Sprintf("Revoked %v", serial))
		// incr revoked cert stat
		wfe.Stats.Inc("RevokedCertificates", 1, 1.0)
		response.WriteHeader(http.StatusOK)
	}
}

func (wfe *WebFrontEndImpl) NewCertificate(response http.ResponseWriter, request *http.Request) {
	if request.Method != "POST" {
		wfe.sendError(response, "Method not allowed", request.Method, http.StatusMethodNotAllowed)
		return
	}

	body, key, reg, err := wfe.verifyPOST(request, true)
	if err != nil {
		if err == sql.ErrNoRows {
			wfe.sendError(response, "No registration exists matching provided key", err, http.StatusForbidden)
		} else {
			wfe.sendError(response, "Unable to read/verify body", err, http.StatusBadRequest)
		}
		return
	}
	if reg.Agreement != wfe.SubscriberAgreementURL {
		wfe.sendError(response, "Must agree to subscriber agreement before any further actions", nil, http.StatusForbidden)
		return
	}

	var init core.CertificateRequest
	if err = json.Unmarshal(body, &init); err != nil {
		fmt.Println(err)
		wfe.sendError(response, "Error unmarshaling certificate request", err, http.StatusBadRequest)
		return
	}

	wfe.log.Notice(fmt.Sprintf("Client requested new certificate: %v %v %v",
		request.RemoteAddr, init, key))

	// Create new certificate and return
	// TODO IMPORTANT: The RA trusts the WFE to provide the correct key. If the
	// WFE is compromised, *and* the attacker knows the public key of an account
	// authorized for target site, they could cause issuance for that site by
	// lying to the RA. We should probably pass a copy of the whole rquest to the
	// RA for secondary validation.
	cert, err := wfe.RA.NewCertificate(init, reg.ID)
	if err != nil {
		wfe.sendError(response,
			"Error creating new cert", err,
			http.StatusBadRequest)
		return
	}

	// Make a URL for this certificate.
	// We use only the sequential part of the serial number, because it should
	// uniquely identify the certificate, and this makes it easy for anybody to
	// enumerate and mirror our certificates.
	parsedCertificate, err := x509.ParseCertificate([]byte(cert.DER))
	if err != nil {
		wfe.sendError(response,
			"Error creating new cert", err,
			http.StatusBadRequest)
		return
	}
	serial := parsedCertificate.SerialNumber
	certURL := fmt.Sprintf("%s%016x", wfe.CertBase, serial.Rsh(serial, 64))

	// TODO Content negotiation
	response.Header().Add("Location", certURL)
	response.Header().Add("Link", link(wfe.BaseURL+IssuerPath, "up"))
	response.Header().Set("Content-Type", "application/pkix-cert")
	response.WriteHeader(http.StatusCreated)
	if _, err = response.Write(cert.DER); err != nil {
		wfe.log.Warning(fmt.Sprintf("Could not write response: %s", err))
	}
	// incr cert stat
	wfe.Stats.Inc("Certificates", 1, 1.0)
}

func (wfe *WebFrontEndImpl) Challenge(authz core.Authorization, response http.ResponseWriter, request *http.Request) {
	// Check that the requested challenge exists within the authorization
	found := false
	var challengeIndex int
	for i, challenge := range authz.Challenges {
		tempURL := url.URL(challenge.URI)
		if tempURL.Path == request.URL.Path && tempURL.RawQuery == request.URL.RawQuery {
			found = true
			challengeIndex = i
			break
		}
	}

	if !found {
		wfe.sendError(response, "Unable to find challenge", request.URL.RawQuery, http.StatusNotFound)
		return
	}

	switch request.Method {
	default:
		wfe.sendError(response, "Method not allowed", "", http.StatusMethodNotAllowed)
		return

	case "POST":
		body, _, currReg, err := wfe.verifyPOST(request, true)
		if err != nil {
			if err == sql.ErrNoRows {
				wfe.sendError(response, "No registration exists matching provided key", err, http.StatusForbidden)
			} else {
				wfe.sendError(response, "Unable to read/verify body", err, http.StatusBadRequest)
			}
			return
		}
		if currReg.Agreement != wfe.SubscriberAgreementURL {
			wfe.sendError(response, "Must agree to subscriber agreement before any further actions", nil, http.StatusForbidden)
			return
		}

		// Check that the registration ID matching the key used matches
		// the registration ID on the authz object
		if currReg.ID != authz.RegistrationID {
			wfe.sendError(response, "User registration ID doesn't match registration ID in authorization",
				fmt.Sprintf("User: %v != Authorization: %v", currReg.ID, authz.RegistrationID),
				http.StatusForbidden)
			return
		}

		var challengeResponse core.Challenge
		if err = json.Unmarshal(body, &challengeResponse); err != nil {
			wfe.sendError(response, "Error unmarshaling challenge response", err, http.StatusBadRequest)
			return
		}

		// Ask the RA to update this authorization
		updatedAuthz, err := wfe.RA.UpdateAuthorization(authz, challengeIndex, challengeResponse)
		if err != nil {
			wfe.sendError(response, "Unable to update authorization", err, http.StatusInternalServerError)
			return
{'fraction_non_alphanumeric': 0.12249972970050817, 'fraction_numerical': 0.00805492485674127, 'mean_word_length': 4.338672438672439, 'pattern_counts': {'":': 4, '<': 29, '<?xml version=': 0, '>': 35, 'https://': 10, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5484278', 'n_tokens_mistral': 12936, 'n_tokens_neox': 12071, 'n_words': 3198}