From 41f5788c77ea102f7d5d5aabdd7358ab104dc555 Mon Sep 17 00:00:00 2001 From: "J.C. Jones" Date: Tue, 16 Jun 2015 20:32:09 -0500 Subject: [PATCH] Correct most `go lint` warnings. (274 -> 5) --- analysis/analysis-engine.go | 7 +-- analysis/analysis-engine_test.go | 2 +- ca/certificate-authority-data.go | 2 +- ca/certificate-authority.go | 13 +++--- ca/certificate-authority_test.go | 30 ++++++------- cmd/activity-monitor/main.go | 7 +-- cmd/admin-revoker/main.go | 8 ++-- cmd/boulder-ca/main.go | 2 +- cmd/boulder-ra/main.go | 6 +-- cmd/boulder-va/main.go | 2 +- cmd/boulder-wfe/main.go | 11 ++--- cmd/boulder/main.go | 7 +-- cmd/mkcrl/main.go | 1 + cmd/mkroot/main.go | 1 + cmd/ocsp-responder/main.go | 12 +++-- cmd/ocsp-updater/main.go | 8 ++-- cmd/shell.go | 3 ++ core/challenges.go | 3 ++ core/dns.go | 8 ++-- core/good_key.go | 2 + core/good_key_test.go | 2 +- core/interfaces.go | 6 +++ core/nonce.go | 14 ++++-- core/objects.go | 73 ++++++++++++++++++++----------- core/objects_test.go | 6 +-- core/util.go | 51 +++++++++++++++++---- core/util_test.go | 18 ++++---- mail/mailer.go | 5 +++ policy/policy-authority.go | 55 ++++++++++++++++------- policy/policy-authority_test.go | 15 +++++-- ra/registration-authority.go | 14 +++++- ra/registration-authority_test.go | 18 ++++---- rpc/amqp-rpc.go | 29 +++++++----- rpc/rpc-interfaces.go | 1 - rpc/rpc-wrappers.go | 46 +++++++++++++++++++ rpc/rpc-wrappers_test.go | 10 +---- sa/database.go | 2 +- sa/storage-authority.go | 33 +++++++++----- sa/storage-authority_test.go | 2 +- sa/type-converter.go | 4 +- sa/type-converter_test.go | 4 +- test/mocks.go | 6 +++ test/test-tools.go | 17 +++++++ va/caa-util.go | 2 +- va/validation-authority.go | 10 +++-- va/validation-authority_test.go | 16 +++---- wfe/web-front-end.go | 31 ++++++++++--- wfe/web-front-end_test.go | 4 +- 48 files changed, 434 insertions(+), 195 deletions(-) diff --git a/analysis/analysis-engine.go b/analysis/analysis-engine.go index a1183adf5..60a5a612f 100644 --- a/analysis/analysis-engine.go +++ b/analysis/analysis-engine.go @@ -16,16 +16,17 @@ import ( // This file analyzes messages obtained from the Message Broker to determine // whether the system as a whole is functioning correctly. -// Interface all Analysis Engines share +// AnalysisEngine is the interface all Analysis Engines share type AnalysisEngine interface { ProcessMessage(amqp.Delivery) (err error) } -// An Analysis Engine that just logs to the JSON Logger. +// LoggingAnalysisEngine is an Analysis Engine that just logs to the Logger. type LoggingAnalysisEngine struct { log *blog.AuditLogger } +// ProcessMessage logs the marshaled contents of the AMQP message. func (eng *LoggingAnalysisEngine) ProcessMessage(delivery amqp.Delivery) (err error) { // Send the entire message contents to the syslog server for debugging. encoded, err := json.Marshal(delivery) @@ -38,7 +39,7 @@ func (eng *LoggingAnalysisEngine) ProcessMessage(delivery amqp.Delivery) (err er return } -// Construct a new Analysis Engine. +// NewLoggingAnalysisEngine constructs a new Analysis Engine. func NewLoggingAnalysisEngine() AnalysisEngine { logger := blog.GetAuditLogger() logger.Notice("Analysis Engine Starting") diff --git a/analysis/analysis-engine_test.go b/analysis/analysis-engine_test.go index 1152f878e..aa7c174fa 100644 --- a/analysis/analysis-engine_test.go +++ b/analysis/analysis-engine_test.go @@ -25,7 +25,7 @@ type MockAck struct { // json.Marshall cannot represent a chan, so this will break // the json.Marshal attempt in ProcessMessage and let us get // coverage there. - JsonBreaker chan bool + JSONBreaker chan bool } func (m *MockAck) Ack(tag uint64, multiple bool) error { diff --git a/ca/certificate-authority-data.go b/ca/certificate-authority-data.go index 7e6eb52ba..9270c81b7 100644 --- a/ca/certificate-authority-data.go +++ b/ca/certificate-authority-data.go @@ -64,7 +64,7 @@ func (cadb *CertificateAuthorityDatabaseImpl) CreateTablesIfNotExists() (err err return } -// GetDbMap gets a pointer to the CA DB's GORP map object. +// Begin starts a transaction at the GORP wrapper. func (cadb *CertificateAuthorityDatabaseImpl) Begin() (*gorp.Transaction, error) { return cadb.dbMap.Begin() } diff --git a/ca/certificate-authority.go b/ca/certificate-authority.go index 222586601..e7731a783 100644 --- a/ca/certificate-authority.go +++ b/ca/certificate-authority.go @@ -46,11 +46,14 @@ type Config struct { CFSSL cfsslConfig.Config } +// KeyConfig should contain either a File path to a PEM-format private key, +// or a PKCS11Config defining how to load a module for an HSM. type KeyConfig struct { File string PKCS11 PKCS11Config } +// PKCS11Config defines how to load a module for an HSM. type PKCS11Config struct { Module string Token string @@ -170,12 +173,12 @@ func loadKey(keyConfig KeyConfig) (priv crypto.Signer, err error) { priv, err = helpers.ParsePrivateKeyPEM(keyBytes) return - } else { - pkcs11Config := keyConfig.PKCS11 - priv, err = pkcs11key.New(pkcs11Config.Module, - pkcs11Config.Token, pkcs11Config.PIN, pkcs11Config.Label) - return } + + pkcs11Config := keyConfig.PKCS11 + priv, err = pkcs11key.New(pkcs11Config.Module, + pkcs11Config.Token, pkcs11Config.PIN, pkcs11Config.Label) + return } func loadIssuer(filename string) (issuerCert *x509.Certificate, err error) { diff --git a/ca/certificate-authority_test.go b/ca/certificate-authority_test.go index fa9039420..c55a1e610 100644 --- a/ca/certificate-authority_test.go +++ b/ca/certificate-authority_test.go @@ -25,7 +25,7 @@ import ( "github.com/letsencrypt/boulder/test" ) -var CA_KEY_PEM = "-----BEGIN RSA PRIVATE KEY-----\n" + +var CAkeyPEM = "-----BEGIN RSA PRIVATE KEY-----\n" + "MIIJKQIBAAKCAgEAqmM0dEf/J9MCk2ItzevL0dKJ84lVUtf/vQ7AXFi492vFXc3b\n" + "PrJz2ybtjO08oVkhRrFGGgLufL2JeOBn5pUZQrp6TqyCLoQ4f/yrmu9tCeG8CtDg\n" + "xi6Ye9LjvlchEHhUKhAHc8uL+ablHzWxHTeuhnuThrsLFUcJQWb10U27LiXp3XCW\n" + @@ -77,7 +77,7 @@ var CA_KEY_PEM = "-----BEGIN RSA PRIVATE KEY-----\n" + "huu1W6p9RdxJHgphzmGAvTrOmrDAZeKtubsMS69VZVFjQFa1ZD/VMzWK1X2o\n" + "-----END RSA PRIVATE KEY-----" -var CA_CERT_PEM = "-----BEGIN CERTIFICATE-----\n" + +var CAcertPEM = "-----BEGIN CERTIFICATE-----\n" + "MIIFxDCCA6ygAwIBAgIJALe2d/gZHJqAMA0GCSqGSIb3DQEBCwUAMDExCzAJBgNV\n" + "BAYTAlVTMRAwDgYDVQQKDAdUZXN0IENBMRAwDgYDVQQDDAdUZXN0IENBMB4XDTE1\n" + "MDIxMzAwMzI0NFoXDTI1MDIxMDAwMzI0NFowMTELMAkGA1UEBhMCVVMxEDAOBgNV\n" + @@ -115,7 +115,7 @@ var CA_CERT_PEM = "-----BEGIN CERTIFICATE-----\n" + // * Random public key // * CN = not-example.com // * DNSNames = not-example.com, www.not-example.com -var CN_AND_SAN_CSR_HEX = "308202a130820189020100301a311830160603550403130f6e6f742d6578" + +var CNandSANCSRhex = "308202a130820189020100301a311830160603550403130f6e6f742d6578" + "616d706c652e636f6d30820122300d06092a864886f70d01010105000382" + "010f003082010a0282010100e56ccbe37003c150202e6f543f9eb1d0e590" + "76ac7f1f62654fa82fe131a23c66bd53a2f62ff7852015c84a394e36836d" + @@ -143,7 +143,7 @@ var CN_AND_SAN_CSR_HEX = "308202a130820189020100301a311830160603550403130f6e6f74 // * Random public key // * CN = not-example.com // * DNSNames = [none] -var NO_SAN_CSR_HEX = "3082025f30820147020100301a311830160603550403130f6e6f742d6578" + +var NoSANCSRhex = "3082025f30820147020100301a311830160603550403130f6e6f742d6578" + "616d706c652e636f6d30820122300d06092a864886f70d01010105000382" + "010f003082010a0282010100aa6e56ff24906f93b855e7871dc8411a3cf7" + "678d9563627e8ca37ab17dfe814ef7f828d6aa92b717f0da9df56990b953" + @@ -170,7 +170,7 @@ var NO_SAN_CSR_HEX = "3082025f30820147020100301a311830160603550403130f6e6f742d65 // * C = US // * CN = [none] // * DNSNames = not-example.com -var NO_CN_CSR_HEX = "3082027f30820167020100300d310b300906035504061302555330820122" + +var NoCNCSRhex = "3082027f30820167020100300d310b300906035504061302555330820122" + "300d06092a864886f70d01010105000382010f003082010a0282010100d8" + "b3c11610ce17614f6d78de3f079db430e479c38978da8cd625b7c70dd445" + "57fd99b9831693e6b9b09fb7c74a82058a1f1a4e1e087f04f93aa73bc35a" + @@ -198,7 +198,7 @@ var NO_CN_CSR_HEX = "3082027f30820167020100300d310b30090603550406130255533082012 // * C = US // * CN = [none] // * DNSNames = [none] -var NO_NAME_CSR_HEX = "308202523082013a020100300d310b300906035504061302555330820122" + +var NoNameCSRhex = "308202523082013a020100300d310b300906035504061302555330820122" + "300d06092a864886f70d01010105000382010f003082010a0282010100bc" + "fae49f68f02c42500b2faf251628ee19e8ef048a35fef311c9c419c80606" + "ab37340ad6e25cf4cc63c0283994b4ba705d86950ad5298094e0b9684647" + @@ -223,7 +223,7 @@ var NO_NAME_CSR_HEX = "308202523082013a020100300d310b300906035504061302555330820 // * Random public key // * CN = [none] // * DNSNames = a.example.com, a.example.com -var DUPE_NAME_CSR_HEX = "308202943082017c020100300d310b300906035504061302555330820122" + +var DupeNameCSRhex = "308202943082017c020100300d310b300906035504061302555330820122" + "300d06092a864886f70d01010105000382010f003082010a0282010100ee" + "7d298c2a8237dd84e75e71dcfbbf8e1124327b103b01f3a99bc76b29be64" + "55329dc523ad1372ed12853dc74a775f2c79d1e4e28ae2a3ce69b78ec161" + @@ -251,7 +251,7 @@ var DUPE_NAME_CSR_HEX = "308202943082017c020100300d310b3009060355040613025553308 // * Random pulic key // * CN = [none] // * DNSNames = not-example.com, www.not-example.com, mail.example.com -var TOO_MANY_NAME_CSR_HEX = "308202aa30820192020100300d310b300906035504061302555330820122" + +var TooManyNameCSRhex = "308202aa30820192020100300d310b300906035504061302555330820122" + "300d06092a864886f70d01010105000382010f003082010a0282010100a7" + "75d8f833651d9a4cfa1fa0e134912b772366c7d070ca3183d3c79ffc99bb" + "c706d328c2389b360b99f60e9a447023a019931d410b4cea0eafb7869a6d" + @@ -362,7 +362,7 @@ func TestRevoke(t *testing.T) { ca.SA = storageAuthority ca.MaxKeySize = 4096 - csrDER, _ := hex.DecodeString(CN_AND_SAN_CSR_HEX) + csrDER, _ := hex.DecodeString(CNandSANCSRhex) csr, _ := x509.ParseCertificateRequest(csrDER) certObj, err := ca.IssueCertificate(*csr, 1, FarFuture) test.AssertNotError(t, err, "Failed to sign certificate") @@ -399,7 +399,7 @@ func TestIssueCertificate(t *testing.T) { } */ - csrs := []string{CN_AND_SAN_CSR_HEX, NO_SAN_CSR_HEX, NO_CN_CSR_HEX} + csrs := []string{CNandSANCSRhex, NoSANCSRhex, NoCNCSRhex} for _, csrHEX := range csrs { csrDER, _ := hex.DecodeString(csrHEX) csr, _ := x509.ParseCertificateRequest(csrDER) @@ -467,7 +467,7 @@ func TestRejectNoName(t *testing.T) { ca.MaxKeySize = 4096 // Test that the CA rejects CSRs with no names - csrDER, _ := hex.DecodeString(NO_NAME_CSR_HEX) + csrDER, _ := hex.DecodeString(NoNameCSRhex) csr, _ := x509.ParseCertificateRequest(csrDER) _, err = ca.IssueCertificate(*csr, 1, FarFuture) if err == nil { @@ -482,7 +482,7 @@ func TestRejectTooManyNames(t *testing.T) { ca.SA = storageAuthority // Test that the CA rejects a CSR with too many names - csrDER, _ := hex.DecodeString(TOO_MANY_NAME_CSR_HEX) + csrDER, _ := hex.DecodeString(TooManyNameCSRhex) csr, _ := x509.ParseCertificateRequest(csrDER) _, err = ca.IssueCertificate(*csr, 1, FarFuture) test.Assert(t, err != nil, "Issued certificate with too many names") @@ -496,7 +496,7 @@ func TestDeduplication(t *testing.T) { ca.MaxKeySize = 4096 // Test that the CA collapses duplicate names - csrDER, _ := hex.DecodeString(DUPE_NAME_CSR_HEX) + csrDER, _ := hex.DecodeString(DupeNameCSRhex) csr, _ := x509.ParseCertificateRequest(csrDER) cert, err := ca.IssueCertificate(*csr, 1, FarFuture) test.AssertNotError(t, err, "Failed to gracefully handle a CSR with duplicate names") @@ -525,13 +525,13 @@ func TestRejectValidityTooLong(t *testing.T) { ca.MaxKeySize = 4096 // Test that the CA rejects CSRs that would expire after the intermediate cert - csrDER, _ := hex.DecodeString(NO_CN_CSR_HEX) + csrDER, _ := hex.DecodeString(NoCNCSRhex) csr, _ := x509.ParseCertificateRequest(csrDER) _, err = ca.IssueCertificate(*csr, 1, FarPast) test.Assert(t, err == nil, "Can issue a certificate that expires after the underlying authorization.") // Test that the CA rejects CSRs that would expire after the intermediate cert - csrDER, _ = hex.DecodeString(NO_CN_CSR_HEX) + csrDER, _ = hex.DecodeString(NoCNCSRhex) csr, _ = x509.ParseCertificateRequest(csrDER) ca.NotAfter = time.Now() _, err = ca.IssueCertificate(*csr, 1, FarFuture) diff --git a/cmd/activity-monitor/main.go b/cmd/activity-monitor/main.go index 0225fa6b9..f60e44aec 100644 --- a/cmd/activity-monitor/main.go +++ b/cmd/activity-monitor/main.go @@ -22,6 +22,7 @@ import ( blog "github.com/letsencrypt/boulder/log" ) +// Constants for AMQP const ( QueueName = "Monitor" AmqpExchange = "boulder" @@ -37,17 +38,17 @@ const ( AmqpImmediate = false ) -var openCalls int64 = 0 +var openCalls int64 func timeDelivery(d amqp.Delivery, stats statsd.Statter, deliveryTimings map[string]time.Time) { // If d is a call add to deliveryTimings and increment openCalls, if it is a // response then get time.Since original call from deliveryTiming, send timing metric, and // decrement openCalls, in both cases send the gauges RpcCallsOpen and RpcBodySize if d.ReplyTo != "" { - openCalls += 1 + openCalls++ deliveryTimings[fmt.Sprintf("%s:%s", d.CorrelationId, d.ReplyTo)] = time.Now() } else { - openCalls -= 1 + openCalls-- rpcSent := deliveryTimings[fmt.Sprintf("%s:%s", d.CorrelationId, d.RoutingKey)] if rpcSent != *new(time.Time) { respTime := time.Since(rpcSent) diff --git a/cmd/admin-revoker/main.go b/cmd/admin-revoker/main.go index 67f93fa9c..721b59a74 100644 --- a/cmd/admin-revoker/main.go +++ b/cmd/admin-revoker/main.go @@ -31,7 +31,7 @@ import ( gorp "github.com/letsencrypt/boulder/Godeps/_workspace/src/gopkg.in/gorp.v1" ) -var reasons map[int]string = map[int]string{ +var reasons = map[int]string{ 0: "unspecified", 1: "keyCompromise", 2: "cACompromise", @@ -69,7 +69,7 @@ func setupContext(context *cli.Context) (rpc.CertificateAuthorityClient, *blog.A ch := cmd.AmqpChannel(c.AMQP.Server) - caRPC, err := rpc.NewAmqpRPCCLient("revoker->CA", c.AMQP.CA.Server, ch) + caRPC, err := rpc.NewAmqpRPCClient("revoker->CA", c.AMQP.CA.Server, ch) cmd.FailOnError(err, "Unable to create RPC client") cac, err := rpc.NewCertificateAuthorityClient(caRPC) @@ -147,7 +147,7 @@ func revokeByReg(regID int, reasonCode int, deny bool, cac rpc.CertificateAuthor return } -var version string = "0.0.1" +var version = "0.0.1" func main() { app := cli.NewApp() @@ -231,7 +231,7 @@ func main() { Usage: "List all revocation reason codes", Action: func(c *cli.Context) { var codes []int - for k, _ := range reasons { + for k := range reasons { codes = append(codes, k) } sort.Ints(codes) diff --git a/cmd/boulder-ca/main.go b/cmd/boulder-ca/main.go index f528ccad8..a7a5e257e 100644 --- a/cmd/boulder-ca/main.go +++ b/cmd/boulder-ca/main.go @@ -48,7 +48,7 @@ func main() { ch := cmd.AmqpChannel(c.AMQP.Server) closeChan := ch.NotifyClose(make(chan *amqp.Error, 1)) - saRPC, err := rpc.NewAmqpRPCCLient("CA->SA", c.AMQP.SA.Server, ch) + saRPC, err := rpc.NewAmqpRPCClient("CA->SA", c.AMQP.SA.Server, ch) cmd.FailOnError(err, "Unable to create RPC client") sac, err := rpc.NewStorageAuthorityClient(saRPC) diff --git a/cmd/boulder-ra/main.go b/cmd/boulder-ra/main.go index 94c95708f..aaf4e0c38 100644 --- a/cmd/boulder-ra/main.go +++ b/cmd/boulder-ra/main.go @@ -41,13 +41,13 @@ func main() { ch := cmd.AmqpChannel(c.AMQP.Server) closeChan := ch.NotifyClose(make(chan *amqp.Error, 1)) - vaRPC, err := rpc.NewAmqpRPCCLient("RA->VA", c.AMQP.VA.Server, ch) + vaRPC, err := rpc.NewAmqpRPCClient("RA->VA", c.AMQP.VA.Server, ch) cmd.FailOnError(err, "Unable to create RPC client") - caRPC, err := rpc.NewAmqpRPCCLient("RA->CA", c.AMQP.CA.Server, ch) + caRPC, err := rpc.NewAmqpRPCClient("RA->CA", c.AMQP.CA.Server, ch) cmd.FailOnError(err, "Unable to create RPC client") - saRPC, err := rpc.NewAmqpRPCCLient("RA->SA", c.AMQP.SA.Server, ch) + saRPC, err := rpc.NewAmqpRPCClient("RA->SA", c.AMQP.SA.Server, ch) cmd.FailOnError(err, "Unable to create RPC client") vac, err := rpc.NewValidationAuthorityClient(vaRPC) diff --git a/cmd/boulder-va/main.go b/cmd/boulder-va/main.go index fe1a23692..83b68d97b 100644 --- a/cmd/boulder-va/main.go +++ b/cmd/boulder-va/main.go @@ -44,7 +44,7 @@ func main() { ch := cmd.AmqpChannel(c.AMQP.Server) closeChan := ch.NotifyClose(make(chan *amqp.Error, 1)) - raRPC, err := rpc.NewAmqpRPCCLient("VA->RA", c.AMQP.RA.Server, ch) + raRPC, err := rpc.NewAmqpRPCClient("VA->RA", c.AMQP.RA.Server, ch) cmd.FailOnError(err, "Unable to create RPC client") rac, err := rpc.NewRegistrationAuthorityClient(raRPC) diff --git a/cmd/boulder-wfe/main.go b/cmd/boulder-wfe/main.go index 52c4fafea..8ae754831 100644 --- a/cmd/boulder-wfe/main.go +++ b/cmd/boulder-wfe/main.go @@ -23,10 +23,10 @@ func setupWFE(c cmd.Config) (rpc.RegistrationAuthorityClient, rpc.StorageAuthori ch := cmd.AmqpChannel(c.AMQP.Server) closeChan := ch.NotifyClose(make(chan *amqp.Error, 1)) - raRPC, err := rpc.NewAmqpRPCCLient("WFE->RA", c.AMQP.RA.Server, ch) + raRPC, err := rpc.NewAmqpRPCClient("WFE->RA", c.AMQP.RA.Server, ch) cmd.FailOnError(err, "Unable to create RPC client") - saRPC, err := rpc.NewAmqpRPCCLient("WFE->SA", c.AMQP.SA.Server, ch) + saRPC, err := rpc.NewAmqpRPCClient("WFE->SA", c.AMQP.SA.Server, ch) cmd.FailOnError(err, "Unable to create RPC client") rac, err := rpc.NewRegistrationAuthorityClient(raRPC) @@ -43,17 +43,18 @@ type timedHandler struct { stats statsd.Statter } -var openConnections int64 = 0 +var openConnections int64 +// HandlerTimer monitors HTTP performance and sends the details to StatsD. func HandlerTimer(handler http.Handler, stats statsd.Statter) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { cStart := time.Now() - openConnections += 1 + openConnections++ stats.Gauge("HttpConnectionsOpen", openConnections, 1.0) handler.ServeHTTP(w, r) - openConnections -= 1 + openConnections-- stats.Gauge("HttpConnectionsOpen", openConnections, 1.0) // (FIX: this doesn't seem to really work at catching errors...) diff --git a/cmd/boulder/main.go b/cmd/boulder/main.go index 062088933..c3c3c3b8c 100644 --- a/cmd/boulder/main.go +++ b/cmd/boulder/main.go @@ -28,17 +28,18 @@ type timedHandler struct { stats statsd.Statter } -var openConnections int64 = 0 +var openConnections int64 +// HandlerTimer monitors HTTP performance and sends the details to StatsD. func HandlerTimer(handler http.Handler, stats statsd.Statter) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { cStart := time.Now() - openConnections += 1 + openConnections++ stats.Gauge("HttpConnectionsOpen", openConnections, 1.0) handler.ServeHTTP(w, r) - openConnections -= 1 + openConnections-- stats.Gauge("HttpConnectionsOpen", openConnections, 1.0) // (FIX: this doesn't seem to really work at catching errors...) diff --git a/cmd/mkcrl/main.go b/cmd/mkcrl/main.go index c5c7f1c9b..815582bb5 100644 --- a/cmd/mkcrl/main.go +++ b/cmd/mkcrl/main.go @@ -22,6 +22,7 @@ var pin = flag.String("pkcs11-pin", "", "PKCS#11 password") var token = flag.String("pkcs11-token", "", "PKCS#11 token name") var label = flag.String("pkcs11-label", "", "PKCS#11 key label") +// Config defines the configuration loaded from listFile. type Config struct { ThisUpdate time.Time NextUpdate time.Time diff --git a/cmd/mkroot/main.go b/cmd/mkroot/main.go index 7c744c6bc..2235cc350 100644 --- a/cmd/mkroot/main.go +++ b/cmd/mkroot/main.go @@ -23,6 +23,7 @@ var pin = flag.String("pkcs11-pin", "", "PKCS#11 password") var token = flag.String("pkcs11-token", "", "PKCS#11 token name") var label = flag.String("pkcs11-label", "", "PKCS#11 key label") +// Config defines the configuration loaded from configFile. type Config struct { Name struct { C string diff --git a/cmd/ocsp-responder/main.go b/cmd/ocsp-responder/main.go index 80823804c..5fd3c7288 100644 --- a/cmd/ocsp-responder/main.go +++ b/cmd/ocsp-responder/main.go @@ -29,17 +29,18 @@ type timedHandler struct { stats statsd.Statter } -var openConnections int64 = 0 +var openConnections int64 +// HandlerTimer monitors HTTP performance and sends the details to StatsD. func HandlerTimer(handler http.Handler, stats statsd.Statter) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { cStart := time.Now() - openConnections += 1 + openConnections++ stats.Gauge("HttpConnectionsOpen", openConnections, 1.0) handler.ServeHTTP(w, r) - openConnections -= 1 + openConnections-- stats.Gauge("HttpConnectionsOpen", openConnections, 1.0) // (FIX: this doesn't seem to really work at catching errors...) @@ -56,6 +57,8 @@ func HandlerTimer(handler http.Handler, stats statsd.Statter) http.Handler { } /* +DBSource maps a given Database schema to a CA Key Hash, so we can pick +from among them when presented with OCSP requests for different certs. We assume that OCSP responses are stored in a very simple database table, with two columns: serialNumber and response @@ -73,11 +76,14 @@ type DBSource struct { caKeyHash []byte } +// NewSourceFromDatabase produces a DBSource representing the binding of a +// given DB schema to a CA key. func NewSourceFromDatabase(dbMap *gorp.DbMap, caKeyHash []byte) (src *DBSource, err error) { src = &DBSource{dbMap: dbMap, caKeyHash: caKeyHash} return } +// Response is called by the HTTP server to handle a new OCSP request. func (src *DBSource) Response(req *ocsp.Request) (response []byte, present bool) { log := blog.GetAuditLogger() diff --git a/cmd/ocsp-updater/main.go b/cmd/ocsp-updater/main.go index 07bc46840..051b1d8f8 100644 --- a/cmd/ocsp-updater/main.go +++ b/cmd/ocsp-updater/main.go @@ -30,7 +30,7 @@ func setupClients(c cmd.Config) (rpc.CertificateAuthorityClient, chan *amqp.Erro ch := cmd.AmqpChannel(c.AMQP.Server) closeChan := ch.NotifyClose(make(chan *amqp.Error, 1)) - caRPC, err := rpc.NewAmqpRPCCLient("OCSP->CA", c.AMQP.CA.Server, ch) + caRPC, err := rpc.NewAmqpRPCClient("OCSP->CA", c.AMQP.CA.Server, ch) cmd.FailOnError(err, "Unable to create RPC client") cac, err := rpc.NewCertificateAuthorityClient(caRPC) @@ -127,10 +127,10 @@ func findStaleResponses(cac rpc.CertificateAuthorityClient, dbMap *gorp.DbMap, o log.Err(fmt.Sprintf("Could not process OCSP Response for %s: %s", status.Serial, err)) tx.Rollback() return err - } else { - log.Info(fmt.Sprintf("OCSP %d: %s OK", i, status.Serial)) - tx.Commit() } + + log.Info(fmt.Sprintf("OCSP %d: %s OK", i, status.Serial)) + tx.Commit() } } diff --git a/cmd/shell.go b/cmd/shell.go index 559257126..a634afab0 100644 --- a/cmd/shell.go +++ b/cmd/shell.go @@ -227,6 +227,7 @@ func RunUntilSignaled(logger *blog.AuditLogger, server *rpc.AmqpRPCServer, close logger.Warning("Reconnecting to AMQP...") } +// ProfileCmd runs forever, sending Go statistics to StatsD. func ProfileCmd(profileName string, stats statsd.Statter) { for { var memoryStats runtime.MemStats @@ -248,6 +249,8 @@ func ProfileCmd(profileName string, stats statsd.Statter) { } } +// LoadCert loads a PEM-formatted certificate from the provided path, returning +// it as a byte array, or an error if it couldn't be decoded. func LoadCert(path string) (cert []byte, err error) { if path == "" { err = errors.New("Issuer certificate was not provided in config.") diff --git a/core/challenges.go b/core/challenges.go index 08a421cfc..e820799dd 100644 --- a/core/challenges.go +++ b/core/challenges.go @@ -11,6 +11,7 @@ import ( blog "github.com/letsencrypt/boulder/log" ) +// SimpleHTTPChallenge constructs a random HTTP challenge func SimpleHTTPChallenge() Challenge { tls := true return Challenge{ @@ -21,6 +22,7 @@ func SimpleHTTPChallenge() Challenge { } } +// DvsniChallenge constructs a random DVSNI challenge func DvsniChallenge() Challenge { nonce := make([]byte, 16) _, err := rand.Read(nonce) @@ -39,6 +41,7 @@ func DvsniChallenge() Challenge { } } +// DNSChallenge constructs a random DNS challenge func DNSChallenge() Challenge { return Challenge{ Type: ChallengeTypeDNS, diff --git a/core/dns.go b/core/dns.go index f12a3f123..7284f7260 100644 --- a/core/dns.go +++ b/core/dns.go @@ -41,16 +41,16 @@ func NewDNSResolver(dialTimeout time.Duration, servers []string) *DNSResolver { // ExchangeOne performs a single DNS exchange with a randomly chosen server // out of the server list, returning the response, time, and error (if any) -func (r *DNSResolver) ExchangeOne(m *dns.Msg) (rsp *dns.Msg, rtt time.Duration, err error) { - if len(r.Servers) < 1 { +func (dnsResolver *DNSResolver) ExchangeOne(m *dns.Msg) (rsp *dns.Msg, rtt time.Duration, err error) { + if len(dnsResolver.Servers) < 1 { err = fmt.Errorf("Not configured with at least one DNS Server") return } // Randomly pick a server - chosenServer := r.Servers[rand.Intn(len(r.Servers))] + chosenServer := dnsResolver.Servers[rand.Intn(len(dnsResolver.Servers))] - return r.DNSClient.Exchange(m, chosenServer) + return dnsResolver.DNSClient.Exchange(m, chosenServer) } // LookupDNSSEC sends the provided DNS message to a randomly chosen server (see diff --git a/core/good_key.go b/core/good_key.go index 61064bb7d..180d10b0b 100644 --- a/core/good_key.go +++ b/core/good_key.go @@ -60,6 +60,7 @@ func GoodKey(key crypto.PublicKey, maxKeySize int) error { } } +// GoodKeyECDSA determines if an ECDSA pubkey meets our requirements func GoodKeyECDSA(key ecdsa.PublicKey, maxKeySize int) (err error) { log := blog.GetAuditLogger() err = fmt.Errorf("ECDSA keys not yet supported") @@ -67,6 +68,7 @@ func GoodKeyECDSA(key ecdsa.PublicKey, maxKeySize int) (err error) { return } +// GoodKeyRSA determines if a RSA pubkey meets our requirements func GoodKeyRSA(key rsa.PublicKey, maxKeySize int) (err error) { log := blog.GetAuditLogger() // Baseline Requirements Appendix A diff --git a/core/good_key_test.go b/core/good_key_test.go index 9c4b55351..ea9a95870 100644 --- a/core/good_key_test.go +++ b/core/good_key_test.go @@ -15,7 +15,7 @@ import ( "github.com/letsencrypt/boulder/test" ) -var maxKeySize int = 2048 +var maxKeySize = 2048 func TestUnknownKeyType(t *testing.T) { notAKey := struct{}{} diff --git a/core/interfaces.go b/core/interfaces.go index 1c7c755e7..b988921fe 100644 --- a/core/interfaces.go +++ b/core/interfaces.go @@ -51,6 +51,7 @@ type WebFrontEnd interface { Cert(response http.ResponseWriter, request *http.Request) } +// RegistrationAuthority defines the public interface for the Boulder RA type RegistrationAuthority interface { // [WebFrontEnd] NewRegistration(Registration) (Registration, error) @@ -74,12 +75,14 @@ type RegistrationAuthority interface { OnValidationUpdate(Authorization) error } +// ValidationAuthority defines the public interface for the Boulder VA type ValidationAuthority interface { // [RegistrationAuthority] UpdateValidations(Authorization, int) error CheckCAARecords(AcmeIdentifier) (bool, bool, error) } +// CertificateAuthority defines the public interface for the Boulder CA type CertificateAuthority interface { // [RegistrationAuthority] IssueCertificate(x509.CertificateRequest, int64, time.Time) (Certificate, error) @@ -87,11 +90,13 @@ type CertificateAuthority interface { GenerateOCSP(OCSPSigningRequest) ([]byte, error) } +// PolicyAuthority defines the public interface for the Boulder PA type PolicyAuthority interface { WillingToIssue(AcmeIdentifier) error ChallengesFor(AcmeIdentifier) ([]Challenge, [][]int) } +// StorageGetter are the Boulder SA's read-only methods type StorageGetter interface { GetRegistration(int64) (Registration, error) GetRegistrationByKey(jose.JsonWebKey) (Registration, error) @@ -102,6 +107,7 @@ type StorageGetter interface { AlreadyDeniedCSR([]string) (bool, error) } +// StorageAdder are the Boulder SA's write/update methods type StorageAdder interface { NewRegistration(Registration) (Registration, error) UpdateRegistration(Registration) error diff --git a/core/nonce.go b/core/nonce.go index 3b368b433..a0bdb48ee 100644 --- a/core/nonce.go +++ b/core/nonce.go @@ -2,6 +2,7 @@ // 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 core import ( @@ -11,8 +12,11 @@ import ( "math/big" ) +// MaxUsed defines the maximum number of Nonces we're willing to hold in +// memory. const MaxUsed = 65536 +// NonceService generates, cancels, and tracks Nonces. type NonceService struct { latest int64 earliest int64 @@ -21,6 +25,7 @@ type NonceService struct { maxUsed int } +// NewNonceService constructs a NonceService with defaults func NewNonceService() NonceService { // XXX ignoring possible error due to entropy starvation key := make([]byte, 16) @@ -44,7 +49,7 @@ func (ns NonceService) encrypt(counter int64) string { // Generate a nonce with upper 4 bytes zero // XXX ignoring possible error due to entropy starvation nonce := make([]byte, 12) - for i := 0; i < 4; i += 1 { + for i := 0; i < 4; i++ { nonce[i] = 0 } rand.Read(nonce[4:]) @@ -70,7 +75,7 @@ func (ns NonceService) decrypt(nonce string) (int64, error) { } n := make([]byte, 12) - for i := 0; i < 4; i += 1 { + for i := 0; i < 4; i++ { n[i] = 0 } copy(n[4:], decoded[:8]) @@ -85,8 +90,9 @@ func (ns NonceService) decrypt(nonce string) (int64, error) { return ctr.Int64(), nil } +// Nonce provides a new Nonce. func (ns *NonceService) Nonce() string { - ns.latest += 1 + ns.latest++ return ns.encrypt(ns.latest) } @@ -100,6 +106,8 @@ func (ns *NonceService) minUsed() int64 { return min } +// Valid determines whether the provided Nonce string is valid, returning +// true if so. func (ns *NonceService) Valid(nonce string) bool { c, err := ns.decrypt(nonce) if err != nil { diff --git a/core/objects.go b/core/objects.go index d746424ba..aa610e96f 100644 --- a/core/objects.go +++ b/core/objects.go @@ -18,11 +18,19 @@ import ( "time" ) +// IdentifierType defines the available identification mechanisms for domains type IdentifierType string + +// AcmeStatus defines the state of a given authorization type AcmeStatus string + +// OCSPStatus defines the state of OCSP for a domain type OCSPStatus string + +// Buffer is a variable-length collection of bytes type Buffer []byte +// These statuses are the states of authorizations const ( StatusUnknown = AcmeStatus("unknown") // Unknown status; the default StatusPending = AcmeStatus("pending") // In process; client has next action @@ -32,11 +40,13 @@ const ( StatusRevoked = AcmeStatus("revoked") // Object no longer valid ) +// These status are the states of OCSP const ( OCSPStatusGood = OCSPStatus("good") OCSPStatusRevoked = OCSPStatus("revoked") ) +// These types are the available challenges const ( ChallengeTypeSimpleHTTP = "simpleHttp" ChallengeTypeDVSNI = "dvsni" @@ -44,6 +54,7 @@ const ( ChallengeTypeRecoveryToken = "recoveryToken" ) +// These types are the available identification mechanisms const ( IdentifierDNS = IdentifierType("dns") ) @@ -88,7 +99,7 @@ type AcmeIdentifier struct { Value string `json:"value"` // The identifier itself } -// An ACME certificate request is just a CSR together with +// CertificateRequest is just a CSR together with // URIs pointing to authorizations that should collectively // authorize the certificate being requsted. // @@ -102,10 +113,11 @@ type CertificateRequest struct { } type rawCertificateRequest struct { - CSR JsonBuffer `json:"csr"` // The encoded CSR + CSR JSONBuffer `json:"csr"` // The encoded CSR Authorizations []AcmeURL `json:"authorizations"` // Authorizations } +// UnmarshalJSON provides an implementation for decoding CertificateRequest objects. func (cr *CertificateRequest) UnmarshalJSON(data []byte) error { var raw rawCertificateRequest if err := json.Unmarshal(data, &raw); err != nil { @@ -122,6 +134,7 @@ func (cr *CertificateRequest) UnmarshalJSON(data []byte) error { return nil } +// MarshalJSON provides an implementation for encoding CertificateRequest objects. func (cr CertificateRequest) MarshalJSON() ([]byte, error) { return json.Marshal(rawCertificateRequest{ CSR: cr.CSR.Raw, @@ -150,6 +163,8 @@ type Registration struct { LockCol int64 `json:"-"` } +// MergeUpdate copies a subset of information from the input Registration +// into this one. func (r *Registration) MergeUpdate(input Registration) { if len(input.Contact) > 0 { r.Contact = input.Contact @@ -160,6 +175,8 @@ func (r *Registration) MergeUpdate(input Registration) { } } +// Challenge is an aggregate of all data needed for any challenges. +// // Rather than define individual types for different types of // challenge, we just throw all the elements into one bucket, // together with the common metadata elements. @@ -190,8 +207,8 @@ type Challenge struct { Nonce string `json:"nonce,omitempty"` } -// Check the sanity of a challenge object before issued to the client (completed = false) -// and before validation (completed = true). +// IsSane checks the sanity of a challenge object before issued to the client +// (completed = false) and before validation (completed = true). func (ch Challenge) IsSane(completed bool) bool { if ch.Status != StatusPending { return false @@ -211,8 +228,8 @@ func (ch Challenge) IsSane(completed bool) bool { return false } // Composed path should be a clean filepath (i.e. no double slashes, dot segments, etc) - vaUrl := fmt.Sprintf("/.well-known/acme-challenge/%s", ch.Path) - if vaUrl != filepath.Clean(vaUrl) { + vaURL := fmt.Sprintf("/.well-known/acme-challenge/%s", ch.Path) + if vaURL != filepath.Clean(vaURL) { return false } } else { @@ -286,7 +303,7 @@ func (ch Challenge) IsSane(completed bool) bool { return true } -// Merge a client-provide response to a challenge with the issued challenge +// MergeResponse copies a subset of client-provided data to the current Challenge. // Note: This method does not update the challenge on the left side of the '.' func (ch Challenge) MergeResponse(resp Challenge) Challenge { // Only override fields that are supposed to be client-provided @@ -305,11 +322,10 @@ func (ch Challenge) MergeResponse(resp Challenge) Challenge { return ch } -// An ACME authorization object represents the authorization -// of an account key holder to act on behalf of a domain. This -// struct is intended to be used both internally and for JSON -// marshaling on the wire. Any fields that should be suppressed -// on the wire (e.g., ID, regID) must be made empty before marshaling. +// Authorization represents the authorization of an account key holder +// to act on behalf of a domain. This struct is intended to be used both +// internally and for JSON marshaling on the wire. Any fields that should be +// suppressed on the wire (e.g., ID, regID) must be made empty before marshaling. type Authorization struct { // An identifier for this authorization, unique across // authorizations and certificates within this instance. @@ -340,28 +356,30 @@ type Authorization struct { Combinations [][]int `json:"combinations,omitempty" db:"combinations"` } -// Fields of this type get encoded and decoded JOSE-style, in base64url encoding +// JSONBuffer fields get encoded and decoded JOSE-style, in base64url encoding // with stripped padding. -type JsonBuffer []byte +type JSONBuffer []byte -// Url-safe base64 encode that strips padding +// URL-safe base64 encode that strips padding func base64URLEncode(data []byte) string { var result = base64.URLEncoding.EncodeToString(data) return strings.TrimRight(result, "=") } -// Url-safe base64 decoder that adds padding +// URL-safe base64 decoder that adds padding func base64URLDecode(data string) ([]byte, error) { var missing = (4 - len(data)%4) % 4 data += strings.Repeat("=", missing) return base64.URLEncoding.DecodeString(data) } -func (jb JsonBuffer) MarshalJSON() (result []byte, err error) { +// MarshalJSON encodes a JSONBuffer for transmission. +func (jb JSONBuffer) MarshalJSON() (result []byte, err error) { return json.Marshal(base64URLEncode(jb)) } -func (jb *JsonBuffer) UnmarshalJSON(data []byte) (err error) { +// UnmarshalJSON decodes a JSONBuffer to an object. +func (jb *JSONBuffer) UnmarshalJSON(data []byte) (err error) { var str string err = json.Unmarshal(data, &str) if err != nil { @@ -383,14 +401,14 @@ type Certificate struct { Serial string `db:"serial"` Digest string `db:"digest"` - DER JsonBuffer `db:"der"` + DER JSONBuffer `db:"der"` Issued time.Time `db:"issued"` Expires time.Time `db:"expires"` } -// Certificate.MatchesCSR tests the contents of a generated certificate to -// make sure that the PublicKey, CommonName, and DNSNames match those provided -// in the CSR that was used to generate the certificate. It also checks the +// MatchesCSR tests the contents of a generated certificate to make sure +// that the PublicKey, CommonName, and DNSNames match those provided in +// the CSR that was used to generate the certificate. It also checks the // following fields for: // * notAfter is after earliestExpiry // * notBefore is not more than 24 hours ago @@ -487,9 +505,10 @@ type CertificateStatus struct { LockCol int64 `json:"-"` } -// A large table of OCSP responses. This contains all historical OCSP -// responses we've signed, is append-only, and is likely to get quite -// large. We'll probably want administratively truncate it at some point. +// OCSPResponse is a (large) table of OCSP responses. This contains all +// historical OCSP responses we've signed, is append-only, and is likely to get +// quite large. +// It must be administratively truncated outside of Boulder. type OCSPResponse struct { ID int `db:"id"` @@ -503,8 +522,9 @@ type OCSPResponse struct { Response []byte `db:"response"` } -// A large table of signed CRLs. This contains all historical CRLs +// CRL is a large table of signed CRLs. This contains all historical CRLs // we've signed, is append-only, and is likely to get quite large. +// It must be administratively truncated outside of Boulder. type CRL struct { // serial: Same as certificate serial. Serial string `db:"serial"` @@ -516,6 +536,7 @@ type CRL struct { CRL string `db:"crl"` } +// DeniedCSR is a list of names we deny issuing. type DeniedCSR struct { ID int `db:"id"` diff --git a/core/objects_test.go b/core/objects_test.go index 4c0933b10..d24df4de5 100644 --- a/core/objects_test.go +++ b/core/objects_test.go @@ -13,7 +13,7 @@ import ( "github.com/letsencrypt/boulder/test" ) -func TestRegistrationUupdate(t *testing.T) { +func TestRegistrationUpdate(t *testing.T) { oldURL, _ := url.Parse("http://old.invalid") newURL, _ := url.Parse("http://new.invalid") @@ -102,9 +102,9 @@ func TestSanityCheck(t *testing.T) { test.Assert(t, !chall.IsSane(true), "IsSane should be false") } -func TestJsonBufferUnmarshal(t *testing.T) { +func TestJSONBufferUnmarshal(t *testing.T) { testStruct := struct { - Buffer JsonBuffer + Buffer JSONBuffer }{} notValidBase64 := []byte(`{"Buffer":"!!!!"}`) diff --git a/core/util.go b/core/util.go index 98929a163..f022d0c88 100644 --- a/core/util.go +++ b/core/util.go @@ -49,12 +49,29 @@ var BuildTime string // Consequently, you should only use this error when Boulder's internal // constraints have been violated. type InternalServerError string + +// NotSupportedError indicates a method is not yet supported type NotSupportedError string + +// MalformedRequestError indicates the user data was improper type MalformedRequestError string + +// UnauthorizedError indicates the user did not satisfactorily prove identity type UnauthorizedError string + +// NotFoundError indicates the destination was unknown. Whoa oh oh ohhh. type NotFoundError string + +// SyntaxError indicates the user improperly formatted their data. type SyntaxError string + +// SignatureValidationError indicates that the user's signature could not +// be verified, either through adversarial activity, or misconfiguration of +// the user client. type SignatureValidationError string + +// CertificateIssuanceError indicates the certificate failed to be issued +// for some reason. type CertificateIssuanceError string func (e InternalServerError) Error() string { return string(e) } @@ -82,10 +99,12 @@ func unpad(x string) string { return strings.Replace(x, "=", "", -1) } +// B64enc encodes a byte array as unpadded, URL-safe Base64 func B64enc(x []byte) string { return unpad(base64.URLEncoding.EncodeToString(x)) } +// B64dec decodes a byte array from unpadded, URL-safe Base64 func B64dec(x string) ([]byte, error) { return base64.URLEncoding.DecodeString(pad(x)) } @@ -104,18 +123,23 @@ func RandomString(byteLength int) string { return B64enc(b) } +// NewToken produces a random string for Challenges, etc. func NewToken() string { return RandomString(32) } // Fingerprints +// Fingerprint256 produces an unpadded, URL-safe Base64-encoded SHA256 digest +// of the data. func Fingerprint256(data []byte) string { d := sha256.New() _, _ = d.Write(data) // Never returns an error return B64enc(d.Sum(nil)) } +// KeyDigest produces a padded, standard Base64-encoded SHA256 digest of a +// provided public key. func KeyDigest(key crypto.PublicKey) (string, error) { switch t := key.(type) { case *jose.JsonWebKey: @@ -134,18 +158,19 @@ func KeyDigest(key crypto.PublicKey) (string, error) { } } +// KeyDigestEquals determines whether two public keys have the same digest. func KeyDigestEquals(j, k crypto.PublicKey) bool { - jDigest, jErr := KeyDigest(j) - kDigest, kErr := KeyDigest(k) + digestJ, errJ := KeyDigest(j) + digestK, errK := KeyDigest(k) // Keys that don't have a valid digest (due to marshalling problems) // are never equal. So, e.g. nil keys are not equal. - if jErr != nil || kErr != nil { + if errJ != nil || errK != nil { return false } - return jDigest == kDigest + return digestJ == digestK } -// URLs that automatically marshal/unmarshal to JSON strings +// AcmeURL is a URL that automatically marshal/unmarshal to JSON strings type AcmeURL url.URL func (u AcmeURL) String() string { @@ -153,6 +178,7 @@ func (u AcmeURL) String() string { return url.String() } +// PathSegments splits an AcmeURL into segments on the '/' characters func (u AcmeURL) PathSegments() (segments []string) { segments = strings.Split(u.Path, "/") if len(segments) > 0 && len(segments[0]) == 0 { @@ -161,11 +187,13 @@ func (u AcmeURL) PathSegments() (segments []string) { return } +// MarshalJSON encodes an AcmeURL for transfer func (u AcmeURL) MarshalJSON() ([]byte, error) { uu := url.URL(u) return json.Marshal(uu.String()) } +// UnmarshalJSON decodes an AcmeURL from transfer func (u *AcmeURL) UnmarshalJSON(data []byte) error { var str string if err := json.Unmarshal(data, &str); err != nil { @@ -177,7 +205,9 @@ func (u *AcmeURL) UnmarshalJSON(data []byte) error { return err } -// The missing CertificateRequest.Verify() method +// VerifyCSR verifies that a Certificate Signature Request is well-formed. +// +// Note: this is the missing CertificateRequest.Verify() method func VerifyCSR(csr *x509.CertificateRequest) error { // Compute the hash of the TBSCertificateRequest var hashID crypto.Hash @@ -237,18 +267,22 @@ func VerifyCSR(csr *x509.CertificateRequest) error { if ecdsa.Verify(ecKey, inputHash, sig.R, sig.S) { return nil - } else { - return errors.New("Invalid ECDSA signature on CSR") } + + return errors.New("Invalid ECDSA signature on CSR") } return errors.New("Unsupported CSR signing algorithm") } +// SerialToString converts a certificate serial number (big.Int) to a String +// consistently. func SerialToString(serial *big.Int) string { return fmt.Sprintf("%032x", serial) } +// StringToSerial converts a string into a certificate serial number (big.Int) +// consistently. func StringToSerial(serial string) (*big.Int, error) { var serialNum big.Int if len(serial) != 32 { @@ -285,6 +319,7 @@ func GetBuildHost() (retID string) { return } +// UniqueNames returns the set of all unique names in the input. func UniqueNames(names []string) (unique []string) { nameMap := make(map[string]int, len(names)) for _, name := range names { diff --git a/core/util_test.go b/core/util_test.go index 48501f575..78b4c4600 100644 --- a/core/util_test.go +++ b/core/util_test.go @@ -51,13 +51,13 @@ func TestBuildID(t *testing.T) { test.AssertEquals(t, "Unspecified", GetBuildID()) } -const JWK_1_JSON = `{ +const JWK1JSON = `{ "kty": "RSA", "n": "vuc785P8lBj3fUxyZchF_uZw6WtbxcorqgTyq-qapF5lrO1U82Tp93rpXlmctj6fyFHBVVB5aXnUHJ7LZeVPod7Wnfl8p5OyhlHQHC8BnzdzCqCMKmWZNX5DtETDId0qzU7dPzh0LP0idt5buU7L9QNaabChw3nnaL47iu_1Di5Wp264p2TwACeedv2hfRDjDlJmaQXuS8Rtv9GnRWyC9JBu7XmGvGDziumnJH7Hyzh3VNu-kSPQD3vuAFgMZS6uUzOztCkT0fpOalZI6hqxtWLvXUMj-crXrn-Maavz8qRhpAyp5kcYk3jiHGgQIi7QSK2JIdRJ8APyX9HlmTN5AQ", "e": "AAEAAQ" }` -const JWK_1_DIGEST = `ul04Iq07ulKnnrebv2hv3yxCGgVvoHs8hjq2tVKx3mc=` -const JWK_2_JSON = `{ +const JWK1Digest = `ul04Iq07ulKnnrebv2hv3yxCGgVvoHs8hjq2tVKx3mc=` +const JWK2JSON = `{ "kty":"RSA", "n":"yTsLkI8n4lg9UuSKNRC0UPHsVjNdCYk8rGXIqeb_rRYaEev3D9-kxXY8HrYfGkVt5CiIVJ-n2t50BKT8oBEMuilmypSQqJw0pCgtUm-e6Z0Eg3Ly6DMXFlycyikegiZ0b-rVX7i5OCEZRDkENAYwFNX4G7NNCwEZcH7HUMUmty9dchAqDS9YWzPh_dde1A9oy9JMH07nRGDcOzIh1rCPwc71nwfPPYeeS4tTvkjanjeigOYBFkBLQuv7iBB4LPozsGF1XdoKiIIi-8ye44McdhOTPDcQp3xKxj89aO02pQhBECv61rmbPinvjMG9DYxJmZvjsKF4bN2oy0DxdC1jDw", "e":"AAEAAQ" @@ -66,13 +66,13 @@ const JWK_2_JSON = `{ func TestKeyDigest(t *testing.T) { // Test with JWK (value, reference, and direct) var jwk jose.JsonWebKey - json.Unmarshal([]byte(JWK_1_JSON), &jwk) + json.Unmarshal([]byte(JWK1JSON), &jwk) digest, err := KeyDigest(jwk) - test.Assert(t, err == nil && digest == JWK_1_DIGEST, "Failed to digest JWK by value") + test.Assert(t, err == nil && digest == JWK1Digest, "Failed to digest JWK by value") digest, err = KeyDigest(&jwk) - test.Assert(t, err == nil && digest == JWK_1_DIGEST, "Failed to digest JWK by reference") + test.Assert(t, err == nil && digest == JWK1Digest, "Failed to digest JWK by reference") digest, err = KeyDigest(jwk.Key) - test.Assert(t, err == nil && digest == JWK_1_DIGEST, "Failed to digest bare key") + test.Assert(t, err == nil && digest == JWK1Digest, "Failed to digest bare key") // Test with unknown key type digest, err = KeyDigest(struct{}{}) @@ -81,8 +81,8 @@ func TestKeyDigest(t *testing.T) { func TestKeyDigestEquals(t *testing.T) { var jwk1, jwk2 jose.JsonWebKey - json.Unmarshal([]byte(JWK_1_JSON), &jwk1) - json.Unmarshal([]byte(JWK_2_JSON), &jwk2) + json.Unmarshal([]byte(JWK1JSON), &jwk1) + json.Unmarshal([]byte(JWK2JSON), &jwk2) test.Assert(t, KeyDigestEquals(jwk1, jwk1), "Key digests for same key should match") test.Assert(t, !KeyDigestEquals(jwk1, jwk2), "Key digests for different keys should not match") diff --git a/mail/mailer.go b/mail/mailer.go index b95a0072b..c84b84bf3 100644 --- a/mail/mailer.go +++ b/mail/mailer.go @@ -9,6 +9,7 @@ import ( "net/smtp" ) +// Mailer defines a mail transfer agent to use for sending mail type Mailer struct { Server string Port string @@ -16,6 +17,8 @@ type Mailer struct { From string } +// NewMailer constructs a Mailer to represent an account at a particular mail +// transfer agent. func NewMailer(server, port, username, password string) Mailer { auth := smtp.PlainAuth("", username, password, server) return Mailer{ @@ -26,6 +29,8 @@ func NewMailer(server, port, username, password string) Mailer { } } +// SendMail sends an email to the provided list of recipients. The email body +// is simple text. func (m *Mailer) SendMail(to []string, msg string) (err error) { err = smtp.SendMail(m.Server+":"+m.Port, m.Auth, m.From, to, []byte(msg)) return diff --git a/policy/policy-authority.go b/policy/policy-authority.go index 0c093a533..31e3666a8 100644 --- a/policy/policy-authority.go +++ b/policy/policy-authority.go @@ -6,7 +6,6 @@ package policy import ( - "errors" "net" "regexp" "strings" @@ -15,6 +14,7 @@ import ( blog "github.com/letsencrypt/boulder/log" ) +// PolicyAuthorityImpl enforces CA policy decisions. type PolicyAuthorityImpl struct { log *blog.AuditLogger @@ -22,6 +22,7 @@ type PolicyAuthorityImpl struct { Blacklist map[string]bool // A blacklist of denied names } +// NewPolicyAuthorityImpl constructs a Policy Authority. func NewPolicyAuthorityImpl() *PolicyAuthorityImpl { logger := blog.GetAuditLogger() logger.Notice("Policy Authority Starting") @@ -52,7 +53,7 @@ func isDNSCharacter(ch byte) bool { // set, then the name is required to not be in the suffix set (i.e., it must // have at least one label beyond any suffix in the set). func suffixMatch(labels []string, suffixSet map[string]bool, properSuffix bool) bool { - for i, _ := range labels { + for i := range labels { if domain := strings.Join(labels[i:], "."); suffixSet[domain] { // If we match on the whole domain, gate on properSuffix return !properSuffix || (i > 0) @@ -61,11 +62,28 @@ func suffixMatch(labels []string, suffixSet map[string]bool, properSuffix bool) return false } -var InvalidIdentifierError = errors.New("Invalid identifier type") -var SyntaxError = errors.New("Syntax error") -var NonPublicError = errors.New("Name does not end in a public suffix") -var BlacklistedError = errors.New("Name is blacklisted") +// InvalidIdentifierError indicates that we didn't understand the IdentifierType +// provided. +type InvalidIdentifierError struct{} +// SyntaxError indicates that the user input was not well formatted. +type SyntaxError struct{} + +// NonPublicError indicates that one or more identifiers were not on the public +// Internet. +type NonPublicError struct{} + +// BlacklistedError indicates we have blacklisted one or more of these identifiers. +type BlacklistedError struct{} + +func (e InvalidIdentifierError) Error() string { return "Invalid identifier type" } +func (e SyntaxError) Error() string { return "Syntax error" } +func (e NonPublicError) Error() string { return "Name does not end in a public suffix" } +func (e BlacklistedError) Error() string { return "Name is blacklisted" } + +// WillingToIssue determines whether the CA is willing to issue for the provided +// identifier. +// // We place several criteria on identifiers we are willing to issue for: // // * MUST self-identify as DNS identifiers @@ -88,59 +106,62 @@ var BlacklistedError = errors.New("Name is blacklisted") // XXX: We should probably fold everything to lower-case somehow. func (pa PolicyAuthorityImpl) WillingToIssue(id core.AcmeIdentifier) error { if id.Type != core.IdentifierDNS { - return InvalidIdentifierError + return InvalidIdentifierError{} } domain := id.Value for _, ch := range []byte(domain) { if !isDNSCharacter(ch) { - return SyntaxError + return SyntaxError{} } } domain = strings.ToLower(domain) if len(domain) > 255 { - return SyntaxError + return SyntaxError{} } if ip := net.ParseIP(domain); ip != nil { - return SyntaxError + return SyntaxError{} } labels := strings.Split(domain, ".") if len(labels) > maxLabels || len(labels) < 2 { - return SyntaxError + return SyntaxError{} } for _, label := range labels { // DNS defines max label length as 63 characters. Some implementations allow // more, but we will be conservative. if len(label) < 1 || len(label) > 63 { - return SyntaxError + return SyntaxError{} } if !dnsLabelRegexp.MatchString(label) { - return SyntaxError + return SyntaxError{} } if punycodeRegexp.MatchString(label) { - return SyntaxError + return SyntaxError{} } } // Require match to PSL, plus at least one label if !suffixMatch(labels, pa.PublicSuffixList, true) { - return NonPublicError + return NonPublicError{} } // Require no match against blacklist if suffixMatch(labels, pa.Blacklist, false) { - return BlacklistedError + return BlacklistedError{} } return nil } -// For now, we just issue DVSNI and SimpleHTTP challenges for everything +// ChallengesFor makes a decision of what challenges, and combinations, are +// acceptable for the given identifier. +// +// Note: Current implementation is static, but future versions may not be. func (pa PolicyAuthorityImpl) ChallengesFor(identifier core.AcmeIdentifier) (challenges []core.Challenge, combinations [][]int) { challenges = []core.Challenge{ core.SimpleHTTPChallenge(), diff --git a/policy/policy-authority_test.go b/policy/policy-authority_test.go index f28c1a842..5f0c8a110 100644 --- a/policy/policy-authority_test.go +++ b/policy/policy-authority_test.go @@ -92,14 +92,17 @@ func TestWillingToIssue(t *testing.T) { // Test for invalid identifier type identifier := core.AcmeIdentifier{Type: "ip", Value: "example.com"} err := pa.WillingToIssue(identifier) - if err != InvalidIdentifierError { + _, ok := err.(InvalidIdentifierError) + if !ok { t.Error("Identifier was not correctly forbidden: ", identifier) } // Test syntax errors for _, domain := range shouldBeSyntaxError { identifier := core.AcmeIdentifier{Type: core.IdentifierDNS, Value: domain} - if err := pa.WillingToIssue(identifier); err != SyntaxError { + err := pa.WillingToIssue(identifier) + _, ok := err.(SyntaxError) + if !ok { t.Error("Identifier was not correctly forbidden: ", identifier, err) } } @@ -107,7 +110,9 @@ func TestWillingToIssue(t *testing.T) { // Test public suffix matching for _, domain := range shouldBeNonPublic { identifier := core.AcmeIdentifier{Type: core.IdentifierDNS, Value: domain} - if err := pa.WillingToIssue(identifier); err != NonPublicError { + err := pa.WillingToIssue(identifier) + _, ok := err.(NonPublicError) + if !ok { t.Error("Identifier was not correctly forbidden: ", identifier, err) } } @@ -115,7 +120,9 @@ func TestWillingToIssue(t *testing.T) { // Test blacklisting for _, domain := range shouldBeBlacklisted { identifier := core.AcmeIdentifier{Type: core.IdentifierDNS, Value: domain} - if err := pa.WillingToIssue(identifier); err != BlacklistedError { + err := pa.WillingToIssue(identifier) + _, ok := err.(BlacklistedError) + if !ok { t.Error("Identifier was not correctly forbidden: ", identifier, err) } } diff --git a/ra/registration-authority.go b/ra/registration-authority.go index 9122a2f8c..2b21116a4 100644 --- a/ra/registration-authority.go +++ b/ra/registration-authority.go @@ -23,7 +23,9 @@ import ( "github.com/letsencrypt/boulder/policy" ) -// All of the fields in RegistrationAuthorityImpl need to be +// RegistrationAuthorityImpl defines an RA. +// +// NOTE: All of the fields in RegistrationAuthorityImpl need to be // populated, or there is a risk of panic. type RegistrationAuthorityImpl struct { CA core.CertificateAuthority @@ -36,6 +38,7 @@ type RegistrationAuthorityImpl struct { MaxKeySize int } +// NewRegistrationAuthorityImpl constructs a new RA object. func NewRegistrationAuthorityImpl() RegistrationAuthorityImpl { logger := blog.GetAuditLogger() logger.Notice("Registration Authority Starting") @@ -103,6 +106,7 @@ type certificateRequestEvent struct { Error string `json:",omitempty"` } +// NewRegistration constructs a new Registration from a request. func (ra *RegistrationAuthorityImpl) NewRegistration(init core.Registration) (reg core.Registration, err error) { if err = core.GoodKey(init.Key.Key, ra.MaxKeySize); err != nil { return core.Registration{}, core.MalformedRequestError(fmt.Sprintf("Invalid public key: %s", err.Error())) @@ -129,6 +133,7 @@ func (ra *RegistrationAuthorityImpl) NewRegistration(init core.Registration) (re return } +// NewAuthorization constuct a new Authz from a request. func (ra *RegistrationAuthorityImpl) NewAuthorization(request core.Authorization, regID int64) (authz core.Authorization, err error) { if regID <= 0 { err = core.MalformedRequestError(fmt.Sprintf("Invalid registration ID: %d", regID)) @@ -202,6 +207,7 @@ func (ra *RegistrationAuthorityImpl) NewAuthorization(request core.Authorization return authz, err } +// NewCertificate requests the issuance of a certificate. func (ra *RegistrationAuthorityImpl) NewCertificate(req core.CertificateRequest, regID int64) (cert core.Certificate, err error) { emptyCert := core.Certificate{} var logEventResult string @@ -306,7 +312,7 @@ func (ra *RegistrationAuthorityImpl) NewCertificate(req core.CertificateRequest, authorizedDomains[authz.Identifier.Value] = true } verificationMethods := []string{} - for method, _ := range verificationMethodSet { + for method := range verificationMethodSet { verificationMethods = append(verificationMethods, method) } logEvent.VerificationMethods = verificationMethods @@ -358,6 +364,7 @@ func (ra *RegistrationAuthorityImpl) NewCertificate(req core.CertificateRequest, return cert, nil } +// UpdateRegistration updates an existing Registration with new values. func (ra *RegistrationAuthorityImpl) UpdateRegistration(base core.Registration, update core.Registration) (reg core.Registration, err error) { base.MergeUpdate(update) @@ -376,6 +383,7 @@ func (ra *RegistrationAuthorityImpl) UpdateRegistration(base core.Registration, return } +// UpdateAuthorization updates an authorization with new values. func (ra *RegistrationAuthorityImpl) UpdateAuthorization(base core.Authorization, challengeIndex int, response core.Challenge) (authz core.Authorization, err error) { // Copy information over that the client is allowed to supply authz = base @@ -399,6 +407,7 @@ func (ra *RegistrationAuthorityImpl) UpdateAuthorization(base core.Authorization return } +// RevokeCertificate terminates trust in the certificate provided. func (ra *RegistrationAuthorityImpl) RevokeCertificate(cert x509.Certificate) (err error) { serialString := core.SerialToString(cert.SerialNumber) err = ra.CA.RevokeCertificate(serialString, 0) @@ -413,6 +422,7 @@ func (ra *RegistrationAuthorityImpl) RevokeCertificate(cert x509.Certificate) (e return err } +// OnValidationUpdate is called when a given Authorization is updated by the VA. func (ra *RegistrationAuthorityImpl) OnValidationUpdate(authz core.Authorization) error { // Consider validation successful if any of the combinations // specified in the authorization has been fulfilled diff --git a/ra/registration-authority_test.go b/ra/registration-authority_test.go index bd1958eee..f659546ad 100644 --- a/ra/registration-authority_test.go +++ b/ra/registration-authority_test.go @@ -154,9 +154,9 @@ func initAuthorities(t *testing.T) (core.CertificateAuthority, *DummyValidationA va := &DummyValidationAuthority{} // PEM files in certificate-authority_test.go - caKeyPEM, _ := pem.Decode([]byte(CA_KEY_PEM)) + caKeyPEM, _ := pem.Decode([]byte(CAkeyPEM)) caKey, _ := x509.ParsePKCS1PrivateKey(caKeyPEM.Bytes) - caCertPEM, _ := pem.Decode([]byte(CA_CERT_PEM)) + caCertPEM, _ := pem.Decode([]byte(CAcertPEM)) caCert, _ := x509.ParseCertificate(caCertPEM.Bytes) basicPolicy := &cfsslConfig.Signing{ Default: &cfsslConfig.SigningProfile{ @@ -184,7 +184,7 @@ func initAuthorities(t *testing.T) (core.CertificateAuthority, *DummyValidationA NotAfter: time.Now().Add(time.Hour * 8761), MaxKeySize: 4096, } - csrDER, _ := hex.DecodeString(CSR_HEX) + csrDER, _ := hex.DecodeString(CSRhex) ExampleCSR, _ = x509.ParseCertificateRequest(csrDER) // This registration implicitly gets ID = 1 @@ -373,8 +373,8 @@ func TestUpdateAuthorization(t *testing.T) { // Verify that the responses are reflected test.Assert(t, len(va.Argument.Challenges) > 0, "Authz passed to VA has no challenges") - simpleHttp := va.Argument.Challenges[0] - test.Assert(t, simpleHttp.Path == Response.Path, "simpleHttp changed") + simpleHTTP := va.Argument.Challenges[0] + test.Assert(t, simpleHTTP.Path == Response.Path, "simpleHTTP changed") t.Log("DONE TestUpdateAuthorization") } @@ -503,7 +503,7 @@ func TestNewCertificate(t *testing.T) { t.Log("DONE TestOnValidationUpdate") } -var CA_KEY_PEM = "-----BEGIN RSA PRIVATE KEY-----\n" + +var CAkeyPEM = "-----BEGIN RSA PRIVATE KEY-----\n" + "MIIJKQIBAAKCAgEAqmM0dEf/J9MCk2ItzevL0dKJ84lVUtf/vQ7AXFi492vFXc3b\n" + "PrJz2ybtjO08oVkhRrFGGgLufL2JeOBn5pUZQrp6TqyCLoQ4f/yrmu9tCeG8CtDg\n" + "xi6Ye9LjvlchEHhUKhAHc8uL+ablHzWxHTeuhnuThrsLFUcJQWb10U27LiXp3XCW\n" + @@ -555,7 +555,7 @@ var CA_KEY_PEM = "-----BEGIN RSA PRIVATE KEY-----\n" + "huu1W6p9RdxJHgphzmGAvTrOmrDAZeKtubsMS69VZVFjQFa1ZD/VMzWK1X2o\n" + "-----END RSA PRIVATE KEY-----" -var CA_CERT_PEM = "-----BEGIN CERTIFICATE-----\n" + +var CAcertPEM = "-----BEGIN CERTIFICATE-----\n" + "MIIFxDCCA6ygAwIBAgIJALe2d/gZHJqAMA0GCSqGSIb3DQEBCwUAMDExCzAJBgNV\n" + "BAYTAlVTMRAwDgYDVQQKDAdUZXN0IENBMRAwDgYDVQQDDAdUZXN0IENBMB4XDTE1\n" + "MDIxMzAwMzI0NFoXDTI1MDIxMDAwMzI0NFowMTELMAkGA1UEBhMCVVMxEDAOBgNV\n" + @@ -594,7 +594,7 @@ var CA_CERT_PEM = "-----BEGIN CERTIFICATE-----\n" + // * CN = not-example.com // * DNSNames = not-example.com, www.not-example.com /* -var CSR_HEX = "3082028c30820174020100301a311830160603550403130f" + +var CSRhex = "3082028c30820174020100301a311830160603550403130f" + "6e6f742d6578616d706c652e636f6d30820122300d06092a" + "864886f70d01010105000382010f003082010a0282010100" + "aac67dd1e11fae980048b0ac91be005f21d9df8bb38461cc" + @@ -624,7 +624,7 @@ var CSR_HEX = "3082028c30820174020100301a311830160603550403130f" + "f277b6d23fa24f9b" */ -var CSR_HEX = "308202ae308201960201003027310b300906035504061302" + +var CSRhex = "308202ae308201960201003027310b300906035504061302" + "5553311830160603550403130f6e6f742d6578616d706c65" + "2e636f6d30820122300d06092a864886f70d010101050003" + "82010f003082010a0282010100a4f507b52ca2766e2cea7b" + diff --git a/rpc/amqp-rpc.go b/rpc/amqp-rpc.go index f4b127be7..ebd3416fd 100644 --- a/rpc/amqp-rpc.go +++ b/rpc/amqp-rpc.go @@ -107,7 +107,7 @@ func amqpSubscribe(ch *amqp.Channel, name string, log *blog.AuditLogger) (msgs < return } -// An AMQP-RPC Server listens on a specified queue within an AMQP channel. +// AmqpRPCServer listens on a specified queue within an AMQP channel. // When messages arrive on that queue, it dispatches them based on type, // and returns the response to the ReplyTo queue. // @@ -120,7 +120,7 @@ type AmqpRPCServer struct { dispatchTable map[string]func([]byte) ([]byte, error) } -// Create a new AMQP-RPC server on the given queue and channel. +// NewAmqpRPCServer creates a new RPC server on the given queue and channel. // Note that you must call Start() to actually start the server // listening for requests. func NewAmqpRPCServer(serverQueue string, channel *amqp.Channel) *AmqpRPCServer { @@ -133,11 +133,12 @@ func NewAmqpRPCServer(serverQueue string, channel *amqp.Channel) *AmqpRPCServer } } +// Handle registers a function to handle a particular method. func (rpc *AmqpRPCServer) Handle(method string, handler func([]byte) ([]byte, error)) { rpc.dispatchTable[method] = handler } -// A JSON wrapper for error as it cannot be un/marshalled +// RPCError is a JSON wrapper for error as it cannot be un/marshalled // due to type interface{}. type RPCError struct { Value string `json:"value"` @@ -198,12 +199,14 @@ func unwrapError(rpcError RPCError) (err error) { return } +// RPCResponse is a stuct for wire-representation of response messages +// used by DispatchSync type RPCResponse struct { ReturnVal []byte `json:"returnVal,omitempty"` Error RPCError `json:"error,omitempty"` } -// Starts the AMQP-RPC server running in a separate thread. +// Start starts the AMQP-RPC server running in a separate thread. // There is currently no Stop() method. func (rpc *AmqpRPCServer) Start() (err error) { msgs, err := amqpSubscribe(rpc.channel, rpc.serverQueue, rpc.log) @@ -246,8 +249,8 @@ func (rpc *AmqpRPCServer) Start() (err error) { return } -// An AMQP-RPC client sends requests to a specific server queue, -// and uses a dedicated response queue for responses. +// AmqpRPCCLient is an AMQP-RPC client that sends requests to a specific server +// queue, and uses a dedicated response queue for responses. // // To implement specific functionality, using code uses the Dispatch() // method to send a method name and body, and get back a response. So @@ -273,7 +276,8 @@ type AmqpRPCCLient struct { log *blog.AuditLogger } -func NewAmqpRPCCLient(clientQueuePrefix, serverQueue string, channel *amqp.Channel) (rpc *AmqpRPCCLient, err error) { +// NewAmqpRPCClient constructs an RPC client using AMQP +func NewAmqpRPCClient(clientQueuePrefix, serverQueue string, channel *amqp.Channel) (rpc *AmqpRPCCLient, err error) { hostname, err := os.Hostname() if err != nil { return nil, err @@ -316,10 +320,15 @@ func NewAmqpRPCCLient(clientQueuePrefix, serverQueue string, channel *amqp.Chann return rpc, err } +// SetTimeout configures the maximum time DispatchSync will wait for a response +// before returning an error. func (rpc *AmqpRPCCLient) SetTimeout(ttl time.Duration) { rpc.timeout = ttl } +// Dispatch sends a body to the destination, and returns a response channel +// that can be used to monitor for responses, or discarded for one-shot +// actions. func (rpc *AmqpRPCCLient) Dispatch(method string, body []byte) chan []byte { // Create a channel on which to direct the response // At least in some cases, it's important that this channel @@ -345,6 +354,7 @@ func (rpc *AmqpRPCCLient) Dispatch(method string, body []byte) chan []byte { return responseChan } +// DispatchSync sends a body to the destination, and blocks waiting on a response. func (rpc *AmqpRPCCLient) DispatchSync(method string, body []byte) (response []byte, err error) { select { case jsonResponse := <-rpc.Dispatch(method, body): @@ -365,8 +375,3 @@ func (rpc *AmqpRPCCLient) DispatchSync(method string, body []byte) (response []b return } } - -func (rpc *AmqpRPCCLient) SyncDispatchWithTimeout(method string, body []byte, ttl time.Duration) (response []byte, err error) { - err = errors.New("Not Implemented") - return -} diff --git a/rpc/rpc-interfaces.go b/rpc/rpc-interfaces.go index 003e977db..6e0aed5eb 100644 --- a/rpc/rpc-interfaces.go +++ b/rpc/rpc-interfaces.go @@ -14,7 +14,6 @@ type RPCClient interface { SetTimeout(time.Duration) Dispatch(string, []byte) chan []byte DispatchSync(string, []byte) ([]byte, error) - SyncDispatchWithTimeout(string, []byte, time.Duration) ([]byte, error) } // RPCServer describes the functions an RPC Server performs diff --git a/rpc/rpc-wrappers.go b/rpc/rpc-wrappers.go index d19d7006f..0b6c60607 100644 --- a/rpc/rpc-wrappers.go +++ b/rpc/rpc-wrappers.go @@ -32,6 +32,7 @@ import ( // The WebFrontEnd role does not expose any functionality over RPC, // so it doesn't need wrappers. +// These strings are used by the RPC layer to identify function points. const ( MethodNewRegistration = "NewRegistration" // RA, SA MethodNewAuthorization = "NewAuthorization" // RA @@ -144,6 +145,7 @@ func errorCondition(method string, err error, obj interface{}) { log.Audit(fmt.Sprintf("Error condition. method: %s err: %s data: %+v", method, err, obj)) } +// NewRegistrationAuthorityServer constructs an RPC server func NewRegistrationAuthorityServer(rpc RPCServer, impl core.RegistrationAuthority) error { log := blog.GetAuditLogger() @@ -289,15 +291,18 @@ func NewRegistrationAuthorityServer(rpc RPCServer, impl core.RegistrationAuthori return nil } +// RegistrationAuthorityClient represents an RA RPC client type RegistrationAuthorityClient struct { rpc RPCClient } +// NewRegistrationAuthorityClient constructs an RPC client func NewRegistrationAuthorityClient(client RPCClient) (rac RegistrationAuthorityClient, err error) { rac = RegistrationAuthorityClient{rpc: client} return } +// NewRegistration sends a New Registration request func (rac RegistrationAuthorityClient) NewRegistration(reg core.Registration) (newReg core.Registration, err error) { data, err := json.Marshal(registrationRequest{reg}) if err != nil { @@ -313,6 +318,7 @@ func (rac RegistrationAuthorityClient) NewRegistration(reg core.Registration) (n return } +// NewAuthorization sends a New Authorization request func (rac RegistrationAuthorityClient) NewAuthorization(authz core.Authorization, regID int64) (newAuthz core.Authorization, err error) { data, err := json.Marshal(authorizationRequest{authz, regID}) if err != nil { @@ -328,6 +334,7 @@ func (rac RegistrationAuthorityClient) NewAuthorization(authz core.Authorization return } +// NewCertificate sends a New Certificate request func (rac RegistrationAuthorityClient) NewCertificate(cr core.CertificateRequest, regID int64) (cert core.Certificate, err error) { data, err := json.Marshal(certificateRequest{cr, regID}) if err != nil { @@ -343,6 +350,7 @@ func (rac RegistrationAuthorityClient) NewCertificate(cr core.CertificateRequest return } +// UpdateRegistration sends an Update Registration request func (rac RegistrationAuthorityClient) UpdateRegistration(base core.Registration, update core.Registration) (newReg core.Registration, err error) { var urReq updateRegistrationRequest urReq.Base = base @@ -362,6 +370,7 @@ func (rac RegistrationAuthorityClient) UpdateRegistration(base core.Registration return } +// UpdateAuthorization sends an Update Authorization request func (rac RegistrationAuthorityClient) UpdateAuthorization(authz core.Authorization, index int, response core.Challenge) (newAuthz core.Authorization, err error) { var uaReq updateAuthorizationRequest uaReq.Authz = authz @@ -382,11 +391,13 @@ func (rac RegistrationAuthorityClient) UpdateAuthorization(authz core.Authorizat return } +// RevokeCertificate sends a Revoke Certificate request func (rac RegistrationAuthorityClient) RevokeCertificate(cert x509.Certificate) (err error) { _, err = rac.rpc.DispatchSync(MethodRevokeCertificate, cert.Raw) return } +// OnValidationUpdate senda a notice that a validation has updated func (rac RegistrationAuthorityClient) OnValidationUpdate(authz core.Authorization) (err error) { data, err := json.Marshal(authz) if err != nil { @@ -397,6 +408,8 @@ func (rac RegistrationAuthorityClient) OnValidationUpdate(authz core.Authorizati return } +// NewValidationAuthorityServer constructs an RPC server +// // ValidationAuthorityClient / Server // -> UpdateValidations func NewValidationAuthorityServer(rpc RPCServer, impl core.ValidationAuthority) (err error) { @@ -441,15 +454,18 @@ func NewValidationAuthorityServer(rpc RPCServer, impl core.ValidationAuthority) return nil } +// ValidationAuthorityClient represents an RPC client for the VA type ValidationAuthorityClient struct { rpc RPCClient } +// NewValidationAuthorityClient constructs an RPC client func NewValidationAuthorityClient(client RPCClient) (vac ValidationAuthorityClient, err error) { vac = ValidationAuthorityClient{rpc: client} return } +// UpdateValidations sends an Update Validations request func (vac ValidationAuthorityClient) UpdateValidations(authz core.Authorization, index int) error { var vaReq validationRequest vaReq.Authz = authz @@ -463,6 +479,7 @@ func (vac ValidationAuthorityClient) UpdateValidations(authz core.Authorization, return nil } +// CheckCAARecords sends a request to check CAA records func (vac ValidationAuthorityClient) CheckCAARecords(ident core.AcmeIdentifier) (present bool, valid bool, err error) { var caaReq caaRequest caaReq.Ident = ident @@ -487,6 +504,8 @@ func (vac ValidationAuthorityClient) CheckCAARecords(ident core.AcmeIdentifier) return } +// NewCertificateAuthorityServer constructs an RPC server +// // CertificateAuthorityClient / Server // -> IssueCertificate func NewCertificateAuthorityServer(rpc RPCServer, impl core.CertificateAuthority) (err error) { @@ -554,15 +573,18 @@ func NewCertificateAuthorityServer(rpc RPCServer, impl core.CertificateAuthority return nil } +// CertificateAuthorityClient is a client to communicate with the CA. type CertificateAuthorityClient struct { rpc RPCClient } +// NewCertificateAuthorityClient constructs an RPC client func NewCertificateAuthorityClient(client RPCClient) (cac CertificateAuthorityClient, err error) { cac = CertificateAuthorityClient{rpc: client} return } +// IssueCertificate sends a request to issue a certificate func (cac CertificateAuthorityClient) IssueCertificate(csr x509.CertificateRequest, regID int64, earliestExpiry time.Time) (cert core.Certificate, err error) { var icReq issueCertificateRequest icReq.Bytes = csr.Raw @@ -581,6 +603,7 @@ func (cac CertificateAuthorityClient) IssueCertificate(csr x509.CertificateReque return } +// RevokeCertificate sends a request to revoke a certificate func (cac CertificateAuthorityClient) RevokeCertificate(serial string, reasonCode int) (err error) { var revokeReq revokeCertificateRequest revokeReq.Serial = serial @@ -597,6 +620,7 @@ func (cac CertificateAuthorityClient) RevokeCertificate(serial string, reasonCod return } +// GenerateOCSP sends a request to generate an OCSP response func (cac CertificateAuthorityClient) GenerateOCSP(signRequest core.OCSPSigningRequest) (resp []byte, err error) { data, err := json.Marshal(signRequest) if err != nil { @@ -616,6 +640,7 @@ func (cac CertificateAuthorityClient) GenerateOCSP(signRequest core.OCSPSigningR return } +// NewStorageAuthorityServer constructs an RPC server func NewStorageAuthorityServer(rpc RPCServer, impl core.StorageAuthority) error { rpc.Handle(MethodUpdateRegistration, func(req []byte) (response []byte, err error) { var reg core.Registration @@ -874,15 +899,18 @@ func NewStorageAuthorityServer(rpc RPCServer, impl core.StorageAuthority) error return nil } +// StorageAuthorityClient is a client to communicate with the Storage Authority type StorageAuthorityClient struct { rpc RPCClient } +// NewStorageAuthorityClient constructs an RPC client func NewStorageAuthorityClient(client RPCClient) (sac StorageAuthorityClient, err error) { sac = StorageAuthorityClient{rpc: client} return } +// GetRegistration sends a request to get a registration by ID func (cac StorageAuthorityClient) GetRegistration(id int64) (reg core.Registration, err error) { var grReq getRegistrationRequest grReq.ID = id @@ -901,6 +929,7 @@ func (cac StorageAuthorityClient) GetRegistration(id int64) (reg core.Registrati return } +// GetRegistrationByKey sends a request to get a registration by JWK func (cac StorageAuthorityClient) GetRegistrationByKey(key jose.JsonWebKey) (reg core.Registration, err error) { jsonKey, err := key.MarshalJSON() if err != nil { @@ -916,6 +945,7 @@ func (cac StorageAuthorityClient) GetRegistrationByKey(key jose.JsonWebKey) (reg return } +// GetAuthorization sends a request to get an Authorization by ID func (cac StorageAuthorityClient) GetAuthorization(id string) (authz core.Authorization, err error) { jsonAuthz, err := cac.rpc.DispatchSync(MethodGetAuthorization, []byte(id)) if err != nil { @@ -926,6 +956,7 @@ func (cac StorageAuthorityClient) GetAuthorization(id string) (authz core.Author return } +// GetCertificate sends a request to get a Certificate by ID func (cac StorageAuthorityClient) GetCertificate(id string) (cert core.Certificate, err error) { jsonCert, err := cac.rpc.DispatchSync(MethodGetCertificate, []byte(id)) if err != nil { @@ -936,6 +967,8 @@ func (cac StorageAuthorityClient) GetCertificate(id string) (cert core.Certifica return } +// GetCertificateByShortSerial sends a request to search for a certificate by +// the predictable portion of its serial number. func (cac StorageAuthorityClient) GetCertificateByShortSerial(id string) (cert core.Certificate, err error) { jsonCert, err := cac.rpc.DispatchSync(MethodGetCertificateByShortSerial, []byte(id)) if err != nil { @@ -946,6 +979,8 @@ func (cac StorageAuthorityClient) GetCertificateByShortSerial(id string) (cert c return } +// GetCertificateStatus sends a request to obtain the current status of a +// certificate by ID func (cac StorageAuthorityClient) GetCertificateStatus(id string) (status core.CertificateStatus, err error) { jsonStatus, err := cac.rpc.DispatchSync(MethodGetCertificateStatus, []byte(id)) if err != nil { @@ -956,6 +991,7 @@ func (cac StorageAuthorityClient) GetCertificateStatus(id string) (status core.C return } +// MarkCertificateRevoked sends a request to mark a certificate as revoked func (cac StorageAuthorityClient) MarkCertificateRevoked(serial string, ocspResponse []byte, reasonCode int) (err error) { var mcrReq markCertificateRevokedRequest @@ -972,6 +1008,7 @@ func (cac StorageAuthorityClient) MarkCertificateRevoked(serial string, ocspResp return } +// UpdateOCSP sends a request to store an updated OCSP response func (cac StorageAuthorityClient) UpdateOCSP(serial string, ocspResponse []byte) (err error) { var updateOCSPReq updateOCSPRequest @@ -987,6 +1024,7 @@ func (cac StorageAuthorityClient) UpdateOCSP(serial string, ocspResponse []byte) return } +// UpdateRegistration sends a request to store an updated registration func (cac StorageAuthorityClient) UpdateRegistration(reg core.Registration) (err error) { jsonReg, err := json.Marshal(reg) if err != nil { @@ -997,6 +1035,7 @@ func (cac StorageAuthorityClient) UpdateRegistration(reg core.Registration) (err return } +// NewRegistration sends a request to store a new registration func (cac StorageAuthorityClient) NewRegistration(reg core.Registration) (output core.Registration, err error) { jsonReg, err := json.Marshal(reg) if err != nil { @@ -1015,6 +1054,7 @@ func (cac StorageAuthorityClient) NewRegistration(reg core.Registration) (output return output, nil } +// NewPendingAuthorization sends a request to store a pending authorization func (cac StorageAuthorityClient) NewPendingAuthorization(authz core.Authorization) (output core.Authorization, err error) { jsonAuthz, err := json.Marshal(authz) if err != nil { @@ -1032,6 +1072,8 @@ func (cac StorageAuthorityClient) NewPendingAuthorization(authz core.Authorizati return } +// UpdatePendingAuthorization sends a request to update the data in a pending +// authorization func (cac StorageAuthorityClient) UpdatePendingAuthorization(authz core.Authorization) (err error) { jsonAuthz, err := json.Marshal(authz) if err != nil { @@ -1042,6 +1084,8 @@ func (cac StorageAuthorityClient) UpdatePendingAuthorization(authz core.Authoriz return } +// FinalizeAuthorization sends a request to finalize an authorization (convert +// from pending) func (cac StorageAuthorityClient) FinalizeAuthorization(authz core.Authorization) (err error) { jsonAuthz, err := json.Marshal(authz) if err != nil { @@ -1052,6 +1096,7 @@ func (cac StorageAuthorityClient) FinalizeAuthorization(authz core.Authorization return } +// AddCertificate sends a request to record the issuance of a certificate func (cac StorageAuthorityClient) AddCertificate(cert []byte, regID int64) (id string, err error) { var acReq addCertificateRequest acReq.Bytes = cert @@ -1069,6 +1114,7 @@ func (cac StorageAuthorityClient) AddCertificate(cert []byte, regID int64) (id s return } +// AlreadyDeniedCSR sends a request to search for denied names func (cac StorageAuthorityClient) AlreadyDeniedCSR(names []string) (exists bool, err error) { var adcReq alreadyDeniedCSRReq adcReq.Names = names diff --git a/rpc/rpc-wrappers_test.go b/rpc/rpc-wrappers_test.go index fcafa2702..79e12647c 100644 --- a/rpc/rpc-wrappers_test.go +++ b/rpc/rpc-wrappers_test.go @@ -16,7 +16,7 @@ import ( "github.com/letsencrypt/boulder/test" ) -const JWK_1_JSON = `{ +const JWK1JSON = `{ "kty": "RSA", "n": "vuc785P8lBj3fUxyZchF_uZw6WtbxcorqgTyq-qapF5lrO1U82Tp93rpXlmctj6fyFHBVVB5aXnUHJ7LZeVPod7Wnfl8p5OyhlHQHC8BnzdzCqCMKmWZNX5DtETDId0qzU7dPzh0LP0idt5buU7L9QNaabChw3nnaL47iu_1Di5Wp264p2TwACeedv2hfRDjDlJmaQXuS8Rtv9GnRWyC9JBu7XmGvGDziumnJH7Hyzh3VNu-kSPQD3vuAFgMZS6uUzOztCkT0fpOalZI6hqxtWLvXUMj-crXrn-Maavz8qRhpAyp5kcYk3jiHGgQIi7QSK2JIdRJ8APyX9HlmTN5AQ", "e": "AAEAAQ" @@ -59,12 +59,6 @@ func (rpc *MockRPCClient) DispatchSync(method string, body []byte) (response []b return } -func (rpc *MockRPCClient) SyncDispatchWithTimeout(method string, body []byte, ttl time.Duration) (response []byte, err error) { - rpc.LastMethod = method - rpc.LastBody = body - return body, nil -} - func TestRANewRegistration(t *testing.T) { mock := &MockRPCClient{} client, err := NewRegistrationAuthorityClient(mock) @@ -72,7 +66,7 @@ func TestRANewRegistration(t *testing.T) { test.AssertNotNil(t, client, "Client construction") var jwk jose.JsonWebKey - json.Unmarshal([]byte(JWK_1_JSON), &jwk) + json.Unmarshal([]byte(JWK1JSON), &jwk) reg := core.Registration{ ID: 1, diff --git a/sa/database.go b/sa/database.go index d21d21668..79cabee42 100644 --- a/sa/database.go +++ b/sa/database.go @@ -19,7 +19,7 @@ import ( blog "github.com/letsencrypt/boulder/log" ) -var dialectMap map[string]interface{} = map[string]interface{}{ +var dialectMap = map[string]interface{}{ "sqlite3": gorp.SqliteDialect{}, "mysql": gorp.MySQLDialect{Engine: "InnoDB", Encoding: "UTF8"}, "postgres": gorp.PostgresDialect{}, diff --git a/sa/storage-authority.go b/sa/storage-authority.go index b76870b32..199f7dee1 100644 --- a/sa/storage-authority.go +++ b/sa/storage-authority.go @@ -22,6 +22,7 @@ import ( blog "github.com/letsencrypt/boulder/log" ) +// SQLStorageAuthority defines a Storage Authority type SQLStorageAuthority struct { dbMap *gorp.DbMap bucket map[string]interface{} // XXX included only for backward compat @@ -99,6 +100,7 @@ func existingRegistration(tx *gorp.Transaction, id int64) bool { return count > 0 } +// GetRegistration obtains a Registration by ID func (ssa *SQLStorageAuthority) GetRegistration(id int64) (reg core.Registration, err error) { regObj, err := ssa.dbMap.Get(core.Registration{}, id) if err != nil { @@ -117,16 +119,18 @@ func (ssa *SQLStorageAuthority) GetRegistration(id int64) (reg core.Registration return } +// GetRegistrationByKey obtains a Registration by JWK func (ssa *SQLStorageAuthority) GetRegistrationByKey(key jose.JsonWebKey) (reg core.Registration, err error) { - keyJson, err := json.Marshal(key) + keyJSON, err := json.Marshal(key) if err != nil { return } - err = ssa.dbMap.SelectOne(®, "SELECT * FROM registrations WHERE jwk = :key", map[string]interface{}{"key": string(keyJson)}) + err = ssa.dbMap.SelectOne(®, "SELECT * FROM registrations WHERE jwk = :key", map[string]interface{}{"key": string(keyJSON)}) return } +// GetAuthorization obtains an Authorization by ID func (ssa *SQLStorageAuthority) GetAuthorization(id string) (authz core.Authorization, err error) { tx, err := ssa.dbMap.Begin() if err != nil { @@ -220,6 +224,7 @@ func (ssa *SQLStorageAuthority) GetCertificateStatus(serial string) (status core return } +// NewRegistration stores a new Registration func (ssa *SQLStorageAuthority) NewRegistration(reg core.Registration) (core.Registration, error) { tx, err := ssa.dbMap.Begin() if err != nil { @@ -240,8 +245,8 @@ func (ssa *SQLStorageAuthority) NewRegistration(reg core.Registration) (core.Reg func (ssa *SQLStorageAuthority) UpdateOCSP(serial string, ocspResponse []byte) (err error) { status, err := ssa.GetCertificateStatus(serial) if err != nil { - return errors.New(fmt.Sprintf( - "Unable to update OCSP for certificate %s: cert status not found.", serial)) + return fmt.Errorf( + "Unable to update OCSP for certificate %s: cert status not found.", serial) } tx, err := ssa.dbMap.Begin() @@ -275,13 +280,13 @@ func (ssa *SQLStorageAuthority) UpdateOCSP(serial string, ocspResponse []byte) ( // with a timestamp and a reason. func (ssa *SQLStorageAuthority) MarkCertificateRevoked(serial string, ocspResponse []byte, reasonCode int) (err error) { if _, err = ssa.GetCertificate(serial); err != nil { - return errors.New(fmt.Sprintf( - "Unable to mark certificate %s revoked: cert not found.", serial)) + return fmt.Errorf( + "Unable to mark certificate %s revoked: cert not found.", serial) } if _, err = ssa.GetCertificateStatus(serial); err != nil { - return errors.New(fmt.Sprintf( - "Unable to mark certificate %s revoked: cert status not found.", serial)) + return fmt.Errorf( + "Unable to mark certificate %s revoked: cert status not found.", serial) } tx, err := ssa.dbMap.Begin() @@ -321,6 +326,7 @@ func (ssa *SQLStorageAuthority) MarkCertificateRevoked(serial string, ocspRespon return } +// UpdateRegistration stores an updated Registration func (ssa *SQLStorageAuthority) UpdateRegistration(reg core.Registration) (err error) { tx, err := ssa.dbMap.Begin() if err != nil { @@ -343,6 +349,7 @@ func (ssa *SQLStorageAuthority) UpdateRegistration(reg core.Registration) (err e return } +// NewPendingAuthorization stores a new Pending Authorization func (ssa *SQLStorageAuthority) NewPendingAuthorization(authz core.Authorization) (output core.Authorization, err error) { tx, err := ssa.dbMap.Begin() if err != nil { @@ -356,18 +363,19 @@ func (ssa *SQLStorageAuthority) NewPendingAuthorization(authz core.Authorization } // Insert a stub row in pending - pending_authz := pendingauthzModel{Authorization: authz} - err = tx.Insert(&pending_authz) + pendingAuthz := pendingauthzModel{Authorization: authz} + err = tx.Insert(&pendingAuthz) if err != nil { tx.Rollback() return } err = tx.Commit() - output = pending_authz.Authorization + output = pendingAuthz.Authorization return } +// UpdatePendingAuthorization updates a Pending Authorization func (ssa *SQLStorageAuthority) UpdatePendingAuthorization(authz core.Authorization) (err error) { tx, err := ssa.dbMap.Begin() if err != nil { @@ -409,6 +417,7 @@ func (ssa *SQLStorageAuthority) UpdatePendingAuthorization(authz core.Authorizat return } +// FinalizeAuthorization converts a Pending Authorization to a final one func (ssa *SQLStorageAuthority) FinalizeAuthorization(authz core.Authorization) (err error) { tx, err := ssa.dbMap.Begin() if err != nil { @@ -463,6 +472,7 @@ func (ssa *SQLStorageAuthority) FinalizeAuthorization(authz core.Authorization) return } +// AddCertificate stores an issued certificate. func (ssa *SQLStorageAuthority) AddCertificate(certDER []byte, regID int64) (digest string, err error) { var parsedCertificate *x509.Certificate parsedCertificate, err = x509.ParseCertificate(certDER) @@ -512,6 +522,7 @@ func (ssa *SQLStorageAuthority) AddCertificate(certDER []byte, regID int64) (dig return } +// AlreadyDeniedCSR queries to find if the name list has already been denied. func (ssa *SQLStorageAuthority) AlreadyDeniedCSR(names []string) (already bool, err error) { sort.Strings(names) diff --git a/sa/storage-authority_test.go b/sa/storage-authority_test.go index 8bd741c43..101dc7660 100644 --- a/sa/storage-authority_test.go +++ b/sa/storage-authority_test.go @@ -34,7 +34,7 @@ func initSA(t *testing.T) *SQLStorageAuthority { return sa } -var theKey string = `{ +var theKey = `{ "kty": "RSA", "n": "n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqVwGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuCLqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5gHdrNP5zw", "e": "AQAB" diff --git a/sa/type-converter.go b/sa/type-converter.go index 092f221c5..0274ad2f9 100644 --- a/sa/type-converter.go +++ b/sa/type-converter.go @@ -38,7 +38,7 @@ func (tc BoulderTypeConverter) ToDb(val interface{}) (interface{}, error) { return string(t), nil case core.OCSPStatus: return string(t), nil - case core.JsonBuffer: + case core.JSONBuffer: return []byte(t), nil default: return val, nil @@ -48,7 +48,7 @@ func (tc BoulderTypeConverter) ToDb(val interface{}) (interface{}, error) { // FromDb converts a DB representation back into a Boulder object. func (tc BoulderTypeConverter) FromDb(target interface{}) (gorp.CustomScanner, bool) { switch target.(type) { - case *core.AcmeIdentifier, *[]core.Challenge, *[]core.AcmeURL, *[][]int, core.JsonBuffer: + case *core.AcmeIdentifier, *[]core.Challenge, *[]core.AcmeURL, *[][]int, core.JSONBuffer: binder := func(holder, target interface{}) error { s, ok := holder.(*string) if !ok { diff --git a/sa/type-converter_test.go b/sa/type-converter_test.go index b882f2658..87e0643b0 100644 --- a/sa/type-converter_test.go +++ b/sa/type-converter_test.go @@ -15,7 +15,7 @@ import ( jose "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/square/go-jose" ) -const JWK_1_JSON = `{ +const JWK1JSON = `{ "kty": "RSA", "n": "vuc785P8lBj3fUxyZchF_uZw6WtbxcorqgTyq-qapF5lrO1U82Tp93rpXlmctj6fyFHBVVB5aXnUHJ7LZeVPod7Wnfl8p5OyhlHQHC8BnzdzCqCMKmWZNX5DtETDId0qzU7dPzh0LP0idt5buU7L9QNaabChw3nnaL47iu_1Di5Wp264p2TwACeedv2hfRDjDlJmaQXuS8Rtv9GnRWyC9JBu7XmGvGDziumnJH7Hyzh3VNu-kSPQD3vuAFgMZS6uUzOztCkT0fpOalZI6hqxtWLvXUMj-crXrn-Maavz8qRhpAyp5kcYk3jiHGgQIi7QSK2JIdRJ8APyX9HlmTN5AQ", "e": "AAEAAQ" @@ -46,7 +46,7 @@ func TestJsonWebKey(t *testing.T) { tc := BoulderTypeConverter{} var jwk, out jose.JsonWebKey - json.Unmarshal([]byte(JWK_1_JSON), &jwk) + json.Unmarshal([]byte(JWK1JSON), &jwk) marshaledI, err := tc.ToDb(jwk) test.AssertNotError(t, err, "Could not ToDb") diff --git a/test/mocks.go b/test/mocks.go index a4aaf9bcc..5d4336971 100644 --- a/test/mocks.go +++ b/test/mocks.go @@ -8,15 +8,18 @@ package test import ( "database/sql" + // Load SQLite3 for test purposes _ "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/mattn/go-sqlite3" gorp "github.com/letsencrypt/boulder/Godeps/_workspace/src/gopkg.in/gorp.v1" ) +// MockCADatabase is a mock type MockCADatabase struct { db *gorp.DbMap count int64 } +// NewMockCertificateAuthorityDatabase is a mock func NewMockCertificateAuthorityDatabase() (mock *MockCADatabase, err error) { db, err := sql.Open("sqlite3", ":memory:") dbmap := &gorp.DbMap{Db: db, Dialect: gorp.SqliteDialect{}} @@ -24,15 +27,18 @@ func NewMockCertificateAuthorityDatabase() (mock *MockCADatabase, err error) { return mock, err } +// Begin is a mock func (cadb *MockCADatabase) Begin() (*gorp.Transaction, error) { return cadb.db.Begin() } +// IncrementAndGetSerial is a mock func (cadb *MockCADatabase) IncrementAndGetSerial(*gorp.Transaction) (int64, error) { cadb.count = cadb.count + 1 return cadb.count, nil } +// CreateTablesIfNotExists is a mock func (cadb *MockCADatabase) CreateTablesIfNotExists() error { return nil } diff --git a/test/test-tools.go b/test/test-tools.go index 5d8ef1d86..8229c137f 100644 --- a/test/test-tools.go +++ b/test/test-tools.go @@ -26,42 +26,50 @@ func caller() string { return fmt.Sprintf("%s:%d:", filename, line) } +// Assert a boolean func Assert(t *testing.T, result bool, message string) { if !result { t.Error(caller(), message) } } +// AssertNotNil checks an object to be non-nil func AssertNotNil(t *testing.T, obj interface{}, message string) { if obj == nil { t.Error(caller(), message) } } +// AssertNotError checks that err is nil func AssertNotError(t *testing.T, err error, message string) { if err != nil { t.Error(caller(), message, ":", err) } } +// AssertError checks that err is non-nil func AssertError(t *testing.T, err error, message string) { if err == nil { t.Error(caller(), message, ":", err) } } +// AssertEquals uses the equality operator (=) to measure one and two func AssertEquals(t *testing.T, one interface{}, two interface{}) { if one != two { t.Errorf("%s [%v] != [%v]", caller(), one, two) } } +// AssertDeepEquals uses the reflect.DeepEqual method to measure one and two func AssertDeepEquals(t *testing.T, one interface{}, two interface{}) { if !reflect.DeepEqual(one, two) { t.Errorf("%s [%+v] !(deep)= [%+v]", caller(), one, two) } } +// AssertMarshaledEquals marshals one and two to JSON, and then uses +// the equality operator to measure them func AssertMarshaledEquals(t *testing.T, one interface{}, two interface{}) { oneJSON, err := json.Marshal(one) AssertNotError(t, err, "Could not marshal 1st argument") @@ -73,12 +81,15 @@ func AssertMarshaledEquals(t *testing.T, one interface{}, two interface{}) { } } +// AssertNotEquals uses the equality operator to measure that one and two +// are different func AssertNotEquals(t *testing.T, one interface{}, two interface{}) { if one == two { t.Errorf("%s [%v] == [%v]", caller(), one, two) } } +// AssertByteEquals uses bytes.Equal to measure one and two for equality. func AssertByteEquals(t *testing.T, one []byte, two []byte) { if !bytes.Equal(one, two) { t.Errorf("%s Byte [%s] != [%s]", @@ -88,30 +99,36 @@ func AssertByteEquals(t *testing.T, one []byte, two []byte) { } } +// AssertIntEquals uses the equality operator to measure one and two. func AssertIntEquals(t *testing.T, one int, two int) { if one != two { t.Errorf("%s Int [%d] != [%d]", caller(), one, two) } } +// AssertBigIntEquals uses the big.Int.cmp() method to measure whether +// one and two are equal func AssertBigIntEquals(t *testing.T, one *big.Int, two *big.Int) { if one.Cmp(two) != 0 { t.Errorf("%s Int [%d] != [%d]", caller(), one, two) } } +// AssertContains determines whether needle can be found in haystack func AssertContains(t *testing.T, haystack string, needle string) { if !strings.Contains(haystack, needle) { t.Errorf("%s String [%s] does not contain [%s]", caller(), haystack, needle) } } +// AssertNotContains determines if needle is not found in haystack func AssertNotContains(t *testing.T, haystack string, needle string) { if strings.Contains(haystack, needle) { t.Errorf("%s String [%s] contains [%s]", caller(), haystack, needle) } } +// AssertSeverity determines if a string matches the Severity formatting func AssertSeverity(t *testing.T, data string, severity int) { expected := fmt.Sprintf("\"severity\":%d", severity) AssertContains(t, data, expected) diff --git a/va/caa-util.go b/va/caa-util.go index 1552bbbc0..ce79e372e 100644 --- a/va/caa-util.go +++ b/va/caa-util.go @@ -58,7 +58,7 @@ func newCAA(encodedRDATA []byte) *CAA { return &CAA{flag: flag, tag: tag, valueBuf: valueBuf, value: value} } -// Filtered CAA records +// CAASet consists of filtered CAA records type CAASet struct { issue []*CAA issuewild []*CAA diff --git a/va/validation-authority.go b/va/validation-authority.go index 45c3e4bab..7e6b54843 100644 --- a/va/validation-authority.go +++ b/va/validation-authority.go @@ -20,6 +20,7 @@ import ( blog "github.com/letsencrypt/boulder/log" ) +// ValidationAuthorityImpl represents a VA type ValidationAuthorityImpl struct { RA core.RegistrationAuthority log *blog.AuditLogger @@ -28,6 +29,8 @@ type ValidationAuthorityImpl struct { TestMode bool } +// NewValidationAuthorityImpl constructs a new VA, and may place it +// into Test Mode (tm) func NewValidationAuthorityImpl(tm bool) ValidationAuthorityImpl { logger := blog.GetAuditLogger() logger.Notice("Validation Authority Starting") @@ -131,8 +134,8 @@ func (va ValidationAuthorityImpl) validateDvsni(identifier core.AcmeIdentifier, return challenge, err } - const DVSNI_SUFFIX = ".acme.invalid" - nonceName := challenge.Nonce + DVSNI_SUFFIX + const DVSNIsuffix = ".acme.invalid" + nonceName := challenge.Nonce + DVSNIsuffix R, err := core.B64dec(challenge.R) if err != nil { @@ -266,12 +269,13 @@ func (va ValidationAuthorityImpl) validate(authz core.Authorization, challengeIn va.RA.OnValidationUpdate(authz) } +// UpdateValidations runs the validate() method asynchronously using goroutines. func (va ValidationAuthorityImpl) UpdateValidations(authz core.Authorization, challengeIndex int) error { go va.validate(authz, challengeIndex) return nil } -// CheckCAA verifies that, if the indicated subscriber domain has any CAA +// CheckCAARecords verifies that, if the indicated subscriber domain has any CAA // records, they authorize the configured CA domain to issue a certificate func (va *ValidationAuthorityImpl) CheckCAARecords(identifier core.AcmeIdentifier) (present, valid bool, err error) { domain := strings.ToLower(identifier.Value) diff --git a/va/validation-authority_test.go b/va/validation-authority_test.go index 28d185da9..22f22d25e 100644 --- a/va/validation-authority_test.go +++ b/va/validation-authority_test.go @@ -36,19 +36,19 @@ func intFromB64(b64 string) int { return int(bigIntFromB64(b64).Int64()) } -var n *big.Int = bigIntFromB64("n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqVwGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuCLqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5gHdrNP5zw==") -var e int = intFromB64("AQAB") -var d *big.Int = bigIntFromB64("bWUC9B-EFRIo8kpGfh0ZuyGPvMNKvYWNtB_ikiH9k20eT-O1q_I78eiZkpXxXQ0UTEs2LsNRS-8uJbvQ-A1irkwMSMkK1J3XTGgdrhCku9gRldY7sNA_AKZGh-Q661_42rINLRCe8W-nZ34ui_qOfkLnK9QWDDqpaIsA-bMwWWSDFu2MUBYwkHTMEzLYGqOe04noqeq1hExBTHBOBdkMXiuFhUq1BU6l-DqEiWxqg82sXt2h-LMnT3046AOYJoRioz75tSUQfGCshWTBnP5uDjd18kKhyv07lhfSJdrPdM5Plyl21hsFf4L_mHCuoFau7gdsPfHPxxjVOcOpBrQzwQ==") -var p *big.Int = bigIntFromB64("uKE2dh-cTf6ERF4k4e_jy78GfPYUIaUyoSSJuBzp3Cubk3OCqs6grT8bR_cu0Dm1MZwWmtdqDyI95HrUeq3MP15vMMON8lHTeZu2lmKvwqW7anV5UzhM1iZ7z4yMkuUwFWoBvyY898EXvRD-hdqRxHlSqAZ192zB3pVFJ0s7pFc=") -var q *big.Int = bigIntFromB64("uKE2dh-cTf6ERF4k4e_jy78GfPYUIaUyoSSJuBzp3Cubk3OCqs6grT8bR_cu0Dm1MZwWmtdqDyI95HrUeq3MP15vMMON8lHTeZu2lmKvwqW7anV5UzhM1iZ7z4yMkuUwFWoBvyY898EXvRD-hdqRxHlSqAZ192zB3pVFJ0s7pFc=") +var n = bigIntFromB64("n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqVwGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuCLqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5gHdrNP5zw==") +var e = intFromB64("AQAB") +var d = bigIntFromB64("bWUC9B-EFRIo8kpGfh0ZuyGPvMNKvYWNtB_ikiH9k20eT-O1q_I78eiZkpXxXQ0UTEs2LsNRS-8uJbvQ-A1irkwMSMkK1J3XTGgdrhCku9gRldY7sNA_AKZGh-Q661_42rINLRCe8W-nZ34ui_qOfkLnK9QWDDqpaIsA-bMwWWSDFu2MUBYwkHTMEzLYGqOe04noqeq1hExBTHBOBdkMXiuFhUq1BU6l-DqEiWxqg82sXt2h-LMnT3046AOYJoRioz75tSUQfGCshWTBnP5uDjd18kKhyv07lhfSJdrPdM5Plyl21hsFf4L_mHCuoFau7gdsPfHPxxjVOcOpBrQzwQ==") +var p = bigIntFromB64("uKE2dh-cTf6ERF4k4e_jy78GfPYUIaUyoSSJuBzp3Cubk3OCqs6grT8bR_cu0Dm1MZwWmtdqDyI95HrUeq3MP15vMMON8lHTeZu2lmKvwqW7anV5UzhM1iZ7z4yMkuUwFWoBvyY898EXvRD-hdqRxHlSqAZ192zB3pVFJ0s7pFc=") +var q = bigIntFromB64("uKE2dh-cTf6ERF4k4e_jy78GfPYUIaUyoSSJuBzp3Cubk3OCqs6grT8bR_cu0Dm1MZwWmtdqDyI95HrUeq3MP15vMMON8lHTeZu2lmKvwqW7anV5UzhM1iZ7z4yMkuUwFWoBvyY898EXvRD-hdqRxHlSqAZ192zB3pVFJ0s7pFc=") -var TheKey rsa.PrivateKey = rsa.PrivateKey{ +var TheKey = rsa.PrivateKey{ PublicKey: rsa.PublicKey{N: n, E: e}, D: d, Primes: []*big.Int{p, q}, } -var ident core.AcmeIdentifier = core.AcmeIdentifier{Type: core.IdentifierDNS, Value: "localhost"} +var ident = core.AcmeIdentifier{Type: core.IdentifierDNS, Value: "localhost"} const expectedToken = "THETOKEN" const pathWrongToken = "wrongtoken" @@ -545,7 +545,7 @@ func TestDNSValidationLive(t *testing.T) { Value: "good.bin.coffee", } - var badIdent core.AcmeIdentifier = core.AcmeIdentifier{ + var badIdent = core.AcmeIdentifier{ Type: core.IdentifierType("dns"), Value: "bad.bin.coffee", } diff --git a/wfe/web-front-end.go b/wfe/web-front-end.go index 76d8963d2..9d1ebf717 100644 --- a/wfe/web-front-end.go +++ b/wfe/web-front-end.go @@ -27,6 +27,7 @@ import ( blog "github.com/letsencrypt/boulder/log" ) +// Paths are the ACME-spec identified URL path-segments for various methods const ( NewRegPath = "/acme/new-reg" RegPath = "/acme/reg/" @@ -99,6 +100,8 @@ func NewWebFrontEndImpl() WebFrontEndImpl { } } +// HandlePaths configures the HTTP engine to use various functions +// as methods for various ACME-specified paths. func (wfe *WebFrontEndImpl) HandlePaths() { wfe.NewReg = wfe.BaseURL + NewRegPath wfe.RegBase = wfe.BaseURL + RegPath @@ -122,6 +125,7 @@ func (wfe *WebFrontEndImpl) HandlePaths() { // Method implementations +// Index serves a simple identification page. It is not part of the ACME spec. func (wfe *WebFrontEndImpl) Index(response http.ResponseWriter, request *http.Request) { wfe.sendStandardHeaders(response) @@ -167,6 +171,7 @@ type problem struct { Detail string `json:"detail,omitempty"` } +// These are defined problems const ( MalformedProblem = ProblemType("urn:acme:error:malformed") UnauthorizedProblem = ProblemType("urn:acme:error:unauthorized") @@ -240,11 +245,10 @@ func (wfe *WebFrontEndImpl) verifyPOST(request *http.Request, regCheck bool) ([] // registration is an overall failure to verify. if regCheck { return nil, nil, reg, err - } else { - // Otherwise we just return an empty registration. The caller is expected - // to use the returned key instead. - reg = core.Registration{} } + // Otherwise we just return an empty registration. The caller is expected + // to use the returned key instead. + reg = core.Registration{} } return []byte(payload), key, reg, nil @@ -293,6 +297,7 @@ func link(url, relation string) string { return fmt.Sprintf("<%s>;rel=\"%s\"", url, relation) } +// NewRegistration is used by clients to submit a new registration/account func (wfe *WebFrontEndImpl) NewRegistration(response http.ResponseWriter, request *http.Request) { wfe.sendStandardHeaders(response) @@ -356,6 +361,7 @@ func (wfe *WebFrontEndImpl) NewRegistration(response http.ResponseWriter, reques wfe.Stats.Inc("Registrations", 1, 1.0) } +// NewAuthorization is used by clients to submit a new ID Authorization func (wfe *WebFrontEndImpl) NewAuthorization(response http.ResponseWriter, request *http.Request) { wfe.sendStandardHeaders(response) @@ -417,6 +423,7 @@ func (wfe *WebFrontEndImpl) NewAuthorization(response http.ResponseWriter, reque wfe.Stats.Inc("PendingAuthorizations", 1, 1.0) } +// RevokeCertificate is used by clients to request the revocation of a cert. func (wfe *WebFrontEndImpl) RevokeCertificate(response http.ResponseWriter, request *http.Request) { wfe.sendStandardHeaders(response) @@ -435,7 +442,7 @@ func (wfe *WebFrontEndImpl) RevokeCertificate(response http.ResponseWriter, requ } type RevokeRequest struct { - CertificateDER core.JsonBuffer `json:"certificate"` + CertificateDER core.JSONBuffer `json:"certificate"` } var revokeRequest RevokeRequest if err = json.Unmarshal(body, &revokeRequest); err != nil { @@ -496,6 +503,8 @@ func (wfe *WebFrontEndImpl) RevokeCertificate(response http.ResponseWriter, requ } } +// NewCertificate is used by clients to request the issuance of a cert for an +// authorized identifier. func (wfe *WebFrontEndImpl) NewCertificate(response http.ResponseWriter, request *http.Request) { wfe.sendStandardHeaders(response) @@ -684,6 +693,7 @@ func (wfe *WebFrontEndImpl) challenge(authz core.Authorization, response http.Re } } +// Registration is used by a client to submit an update to their registration. func (wfe *WebFrontEndImpl) Registration(response http.ResponseWriter, request *http.Request) { wfe.sendStandardHeaders(response) @@ -759,6 +769,8 @@ func (wfe *WebFrontEndImpl) Registration(response http.ResponseWriter, request * response.Write(jsonReply) } +// Authorization is used by clients to submit an update to one of their +// authorizations. func (wfe *WebFrontEndImpl) Authorization(response http.ResponseWriter, request *http.Request) { wfe.sendStandardHeaders(response) @@ -812,6 +824,8 @@ func (wfe *WebFrontEndImpl) Authorization(response http.ResponseWriter, request var allHex = regexp.MustCompile("^[0-9a-f]+$") +// Certificate is used by clients to request a copy of their current certificate, or to +// request a reissuance of the certificate. func (wfe *WebFrontEndImpl) Certificate(response http.ResponseWriter, request *http.Request) { wfe.sendStandardHeaders(response) @@ -859,9 +873,15 @@ func (wfe *WebFrontEndImpl) Certificate(response http.ResponseWriter, request *h if _, err = response.Write(cert.DER); err != nil { wfe.log.Warning(fmt.Sprintf("Could not write response: %s", err)) } + return + case "POST": + wfe.sendError(response, "Not yet supported", "", http.StatusNotFound) + return } } +// Terms is used by the client to obtain the current Terms of Service / +// Subscriber Agreement to which the subscriber must agree. func (wfe *WebFrontEndImpl) Terms(response http.ResponseWriter, request *http.Request) { wfe.sendStandardHeaders(response) @@ -874,6 +894,7 @@ func (wfe *WebFrontEndImpl) Terms(response http.ResponseWriter, request *http.Re fmt.Fprintf(response, "TODO: Add terms of use here") } +// Issuer obtains the issuer certificate used by this instance of Boulder. func (wfe *WebFrontEndImpl) Issuer(response http.ResponseWriter, request *http.Request) { wfe.sendStandardHeaders(response) diff --git a/wfe/web-front-end_test.go b/wfe/web-front-end_test.go index ef8169c68..35a2ef6f6 100644 --- a/wfe/web-front-end_test.go +++ b/wfe/web-front-end_test.go @@ -710,7 +710,7 @@ func TestRevokeCertificate(t *testing.T) { certBlock, _ := pem.Decode(certPemBytes) test.Assert(t, certBlock != nil, "Failed to decode PEM") var revokeRequest struct { - CertificateDER core.JsonBuffer `json:"certificate"` + CertificateDER core.JSONBuffer `json:"certificate"` } revokeRequest.CertificateDER = certBlock.Bytes revokeRequestJSON, err := json.Marshal(revokeRequest) @@ -767,7 +767,7 @@ func TestRevokeCertificateAlreadyRevoked(t *testing.T) { certBlock, _ := pem.Decode(certPemBytes) test.Assert(t, certBlock != nil, "Failed to decode PEM") var revokeRequest struct { - CertificateDER core.JsonBuffer `json:"certificate"` + CertificateDER core.JSONBuffer `json:"certificate"` } revokeRequest.CertificateDER = certBlock.Bytes revokeRequestJSON, err := json.Marshal(revokeRequest)