Correct most `go lint` warnings. (274 -> 5)

This commit is contained in:
J.C. Jones 2015-06-16 20:32:09 -05:00
parent 383885df08
commit 41f5788c77
48 changed files with 434 additions and 195 deletions

View File

@ -16,16 +16,17 @@ import (
// This file analyzes messages obtained from the Message Broker to determine // This file analyzes messages obtained from the Message Broker to determine
// whether the system as a whole is functioning correctly. // 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 { type AnalysisEngine interface {
ProcessMessage(amqp.Delivery) (err error) 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 { type LoggingAnalysisEngine struct {
log *blog.AuditLogger log *blog.AuditLogger
} }
// ProcessMessage logs the marshaled contents of the AMQP message.
func (eng *LoggingAnalysisEngine) ProcessMessage(delivery amqp.Delivery) (err error) { func (eng *LoggingAnalysisEngine) ProcessMessage(delivery amqp.Delivery) (err error) {
// Send the entire message contents to the syslog server for debugging. // Send the entire message contents to the syslog server for debugging.
encoded, err := json.Marshal(delivery) encoded, err := json.Marshal(delivery)
@ -38,7 +39,7 @@ func (eng *LoggingAnalysisEngine) ProcessMessage(delivery amqp.Delivery) (err er
return return
} }
// Construct a new Analysis Engine. // NewLoggingAnalysisEngine constructs a new Analysis Engine.
func NewLoggingAnalysisEngine() AnalysisEngine { func NewLoggingAnalysisEngine() AnalysisEngine {
logger := blog.GetAuditLogger() logger := blog.GetAuditLogger()
logger.Notice("Analysis Engine Starting") logger.Notice("Analysis Engine Starting")

View File

@ -25,7 +25,7 @@ type MockAck struct {
// json.Marshall cannot represent a chan, so this will break // json.Marshall cannot represent a chan, so this will break
// the json.Marshal attempt in ProcessMessage and let us get // the json.Marshal attempt in ProcessMessage and let us get
// coverage there. // coverage there.
JsonBreaker chan bool JSONBreaker chan bool
} }
func (m *MockAck) Ack(tag uint64, multiple bool) error { func (m *MockAck) Ack(tag uint64, multiple bool) error {

View File

@ -64,7 +64,7 @@ func (cadb *CertificateAuthorityDatabaseImpl) CreateTablesIfNotExists() (err err
return 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) { func (cadb *CertificateAuthorityDatabaseImpl) Begin() (*gorp.Transaction, error) {
return cadb.dbMap.Begin() return cadb.dbMap.Begin()
} }

View File

@ -46,11 +46,14 @@ type Config struct {
CFSSL cfsslConfig.Config 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 { type KeyConfig struct {
File string File string
PKCS11 PKCS11Config PKCS11 PKCS11Config
} }
// PKCS11Config defines how to load a module for an HSM.
type PKCS11Config struct { type PKCS11Config struct {
Module string Module string
Token string Token string
@ -170,13 +173,13 @@ func loadKey(keyConfig KeyConfig) (priv crypto.Signer, err error) {
priv, err = helpers.ParsePrivateKeyPEM(keyBytes) priv, err = helpers.ParsePrivateKeyPEM(keyBytes)
return return
} else { }
pkcs11Config := keyConfig.PKCS11 pkcs11Config := keyConfig.PKCS11
priv, err = pkcs11key.New(pkcs11Config.Module, priv, err = pkcs11key.New(pkcs11Config.Module,
pkcs11Config.Token, pkcs11Config.PIN, pkcs11Config.Label) pkcs11Config.Token, pkcs11Config.PIN, pkcs11Config.Label)
return return
} }
}
func loadIssuer(filename string) (issuerCert *x509.Certificate, err error) { func loadIssuer(filename string) (issuerCert *x509.Certificate, err error) {
if filename == "" { if filename == "" {

View File

@ -25,7 +25,7 @@ import (
"github.com/letsencrypt/boulder/test" "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" + "MIIJKQIBAAKCAgEAqmM0dEf/J9MCk2ItzevL0dKJ84lVUtf/vQ7AXFi492vFXc3b\n" +
"PrJz2ybtjO08oVkhRrFGGgLufL2JeOBn5pUZQrp6TqyCLoQ4f/yrmu9tCeG8CtDg\n" + "PrJz2ybtjO08oVkhRrFGGgLufL2JeOBn5pUZQrp6TqyCLoQ4f/yrmu9tCeG8CtDg\n" +
"xi6Ye9LjvlchEHhUKhAHc8uL+ablHzWxHTeuhnuThrsLFUcJQWb10U27LiXp3XCW\n" + "xi6Ye9LjvlchEHhUKhAHc8uL+ablHzWxHTeuhnuThrsLFUcJQWb10U27LiXp3XCW\n" +
@ -77,7 +77,7 @@ var CA_KEY_PEM = "-----BEGIN RSA PRIVATE KEY-----\n" +
"huu1W6p9RdxJHgphzmGAvTrOmrDAZeKtubsMS69VZVFjQFa1ZD/VMzWK1X2o\n" + "huu1W6p9RdxJHgphzmGAvTrOmrDAZeKtubsMS69VZVFjQFa1ZD/VMzWK1X2o\n" +
"-----END RSA PRIVATE KEY-----" "-----END RSA PRIVATE KEY-----"
var CA_CERT_PEM = "-----BEGIN CERTIFICATE-----\n" + var CAcertPEM = "-----BEGIN CERTIFICATE-----\n" +
"MIIFxDCCA6ygAwIBAgIJALe2d/gZHJqAMA0GCSqGSIb3DQEBCwUAMDExCzAJBgNV\n" + "MIIFxDCCA6ygAwIBAgIJALe2d/gZHJqAMA0GCSqGSIb3DQEBCwUAMDExCzAJBgNV\n" +
"BAYTAlVTMRAwDgYDVQQKDAdUZXN0IENBMRAwDgYDVQQDDAdUZXN0IENBMB4XDTE1\n" + "BAYTAlVTMRAwDgYDVQQKDAdUZXN0IENBMRAwDgYDVQQDDAdUZXN0IENBMB4XDTE1\n" +
"MDIxMzAwMzI0NFoXDTI1MDIxMDAwMzI0NFowMTELMAkGA1UEBhMCVVMxEDAOBgNV\n" + "MDIxMzAwMzI0NFoXDTI1MDIxMDAwMzI0NFowMTELMAkGA1UEBhMCVVMxEDAOBgNV\n" +
@ -115,7 +115,7 @@ var CA_CERT_PEM = "-----BEGIN CERTIFICATE-----\n" +
// * Random public key // * Random public key
// * CN = not-example.com // * CN = not-example.com
// * DNSNames = not-example.com, www.not-example.com // * DNSNames = not-example.com, www.not-example.com
var CN_AND_SAN_CSR_HEX = "308202a130820189020100301a311830160603550403130f6e6f742d6578" + var CNandSANCSRhex = "308202a130820189020100301a311830160603550403130f6e6f742d6578" +
"616d706c652e636f6d30820122300d06092a864886f70d01010105000382" + "616d706c652e636f6d30820122300d06092a864886f70d01010105000382" +
"010f003082010a0282010100e56ccbe37003c150202e6f543f9eb1d0e590" + "010f003082010a0282010100e56ccbe37003c150202e6f543f9eb1d0e590" +
"76ac7f1f62654fa82fe131a23c66bd53a2f62ff7852015c84a394e36836d" + "76ac7f1f62654fa82fe131a23c66bd53a2f62ff7852015c84a394e36836d" +
@ -143,7 +143,7 @@ var CN_AND_SAN_CSR_HEX = "308202a130820189020100301a311830160603550403130f6e6f74
// * Random public key // * Random public key
// * CN = not-example.com // * CN = not-example.com
// * DNSNames = [none] // * DNSNames = [none]
var NO_SAN_CSR_HEX = "3082025f30820147020100301a311830160603550403130f6e6f742d6578" + var NoSANCSRhex = "3082025f30820147020100301a311830160603550403130f6e6f742d6578" +
"616d706c652e636f6d30820122300d06092a864886f70d01010105000382" + "616d706c652e636f6d30820122300d06092a864886f70d01010105000382" +
"010f003082010a0282010100aa6e56ff24906f93b855e7871dc8411a3cf7" + "010f003082010a0282010100aa6e56ff24906f93b855e7871dc8411a3cf7" +
"678d9563627e8ca37ab17dfe814ef7f828d6aa92b717f0da9df56990b953" + "678d9563627e8ca37ab17dfe814ef7f828d6aa92b717f0da9df56990b953" +
@ -170,7 +170,7 @@ var NO_SAN_CSR_HEX = "3082025f30820147020100301a311830160603550403130f6e6f742d65
// * C = US // * C = US
// * CN = [none] // * CN = [none]
// * DNSNames = not-example.com // * DNSNames = not-example.com
var NO_CN_CSR_HEX = "3082027f30820167020100300d310b300906035504061302555330820122" + var NoCNCSRhex = "3082027f30820167020100300d310b300906035504061302555330820122" +
"300d06092a864886f70d01010105000382010f003082010a0282010100d8" + "300d06092a864886f70d01010105000382010f003082010a0282010100d8" +
"b3c11610ce17614f6d78de3f079db430e479c38978da8cd625b7c70dd445" + "b3c11610ce17614f6d78de3f079db430e479c38978da8cd625b7c70dd445" +
"57fd99b9831693e6b9b09fb7c74a82058a1f1a4e1e087f04f93aa73bc35a" + "57fd99b9831693e6b9b09fb7c74a82058a1f1a4e1e087f04f93aa73bc35a" +
@ -198,7 +198,7 @@ var NO_CN_CSR_HEX = "3082027f30820167020100300d310b30090603550406130255533082012
// * C = US // * C = US
// * CN = [none] // * CN = [none]
// * DNSNames = [none] // * DNSNames = [none]
var NO_NAME_CSR_HEX = "308202523082013a020100300d310b300906035504061302555330820122" + var NoNameCSRhex = "308202523082013a020100300d310b300906035504061302555330820122" +
"300d06092a864886f70d01010105000382010f003082010a0282010100bc" + "300d06092a864886f70d01010105000382010f003082010a0282010100bc" +
"fae49f68f02c42500b2faf251628ee19e8ef048a35fef311c9c419c80606" + "fae49f68f02c42500b2faf251628ee19e8ef048a35fef311c9c419c80606" +
"ab37340ad6e25cf4cc63c0283994b4ba705d86950ad5298094e0b9684647" + "ab37340ad6e25cf4cc63c0283994b4ba705d86950ad5298094e0b9684647" +
@ -223,7 +223,7 @@ var NO_NAME_CSR_HEX = "308202523082013a020100300d310b300906035504061302555330820
// * Random public key // * Random public key
// * CN = [none] // * CN = [none]
// * DNSNames = a.example.com, a.example.com // * DNSNames = a.example.com, a.example.com
var DUPE_NAME_CSR_HEX = "308202943082017c020100300d310b300906035504061302555330820122" + var DupeNameCSRhex = "308202943082017c020100300d310b300906035504061302555330820122" +
"300d06092a864886f70d01010105000382010f003082010a0282010100ee" + "300d06092a864886f70d01010105000382010f003082010a0282010100ee" +
"7d298c2a8237dd84e75e71dcfbbf8e1124327b103b01f3a99bc76b29be64" + "7d298c2a8237dd84e75e71dcfbbf8e1124327b103b01f3a99bc76b29be64" +
"55329dc523ad1372ed12853dc74a775f2c79d1e4e28ae2a3ce69b78ec161" + "55329dc523ad1372ed12853dc74a775f2c79d1e4e28ae2a3ce69b78ec161" +
@ -251,7 +251,7 @@ var DUPE_NAME_CSR_HEX = "308202943082017c020100300d310b3009060355040613025553308
// * Random pulic key // * Random pulic key
// * CN = [none] // * CN = [none]
// * DNSNames = not-example.com, www.not-example.com, mail.example.com // * DNSNames = not-example.com, www.not-example.com, mail.example.com
var TOO_MANY_NAME_CSR_HEX = "308202aa30820192020100300d310b300906035504061302555330820122" + var TooManyNameCSRhex = "308202aa30820192020100300d310b300906035504061302555330820122" +
"300d06092a864886f70d01010105000382010f003082010a0282010100a7" + "300d06092a864886f70d01010105000382010f003082010a0282010100a7" +
"75d8f833651d9a4cfa1fa0e134912b772366c7d070ca3183d3c79ffc99bb" + "75d8f833651d9a4cfa1fa0e134912b772366c7d070ca3183d3c79ffc99bb" +
"c706d328c2389b360b99f60e9a447023a019931d410b4cea0eafb7869a6d" + "c706d328c2389b360b99f60e9a447023a019931d410b4cea0eafb7869a6d" +
@ -362,7 +362,7 @@ func TestRevoke(t *testing.T) {
ca.SA = storageAuthority ca.SA = storageAuthority
ca.MaxKeySize = 4096 ca.MaxKeySize = 4096
csrDER, _ := hex.DecodeString(CN_AND_SAN_CSR_HEX) csrDER, _ := hex.DecodeString(CNandSANCSRhex)
csr, _ := x509.ParseCertificateRequest(csrDER) csr, _ := x509.ParseCertificateRequest(csrDER)
certObj, err := ca.IssueCertificate(*csr, 1, FarFuture) certObj, err := ca.IssueCertificate(*csr, 1, FarFuture)
test.AssertNotError(t, err, "Failed to sign certificate") 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 { for _, csrHEX := range csrs {
csrDER, _ := hex.DecodeString(csrHEX) csrDER, _ := hex.DecodeString(csrHEX)
csr, _ := x509.ParseCertificateRequest(csrDER) csr, _ := x509.ParseCertificateRequest(csrDER)
@ -467,7 +467,7 @@ func TestRejectNoName(t *testing.T) {
ca.MaxKeySize = 4096 ca.MaxKeySize = 4096
// Test that the CA rejects CSRs with no names // Test that the CA rejects CSRs with no names
csrDER, _ := hex.DecodeString(NO_NAME_CSR_HEX) csrDER, _ := hex.DecodeString(NoNameCSRhex)
csr, _ := x509.ParseCertificateRequest(csrDER) csr, _ := x509.ParseCertificateRequest(csrDER)
_, err = ca.IssueCertificate(*csr, 1, FarFuture) _, err = ca.IssueCertificate(*csr, 1, FarFuture)
if err == nil { if err == nil {
@ -482,7 +482,7 @@ func TestRejectTooManyNames(t *testing.T) {
ca.SA = storageAuthority ca.SA = storageAuthority
// Test that the CA rejects a CSR with too many names // 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) csr, _ := x509.ParseCertificateRequest(csrDER)
_, err = ca.IssueCertificate(*csr, 1, FarFuture) _, err = ca.IssueCertificate(*csr, 1, FarFuture)
test.Assert(t, err != nil, "Issued certificate with too many names") test.Assert(t, err != nil, "Issued certificate with too many names")
@ -496,7 +496,7 @@ func TestDeduplication(t *testing.T) {
ca.MaxKeySize = 4096 ca.MaxKeySize = 4096
// Test that the CA collapses duplicate names // Test that the CA collapses duplicate names
csrDER, _ := hex.DecodeString(DUPE_NAME_CSR_HEX) csrDER, _ := hex.DecodeString(DupeNameCSRhex)
csr, _ := x509.ParseCertificateRequest(csrDER) csr, _ := x509.ParseCertificateRequest(csrDER)
cert, err := ca.IssueCertificate(*csr, 1, FarFuture) cert, err := ca.IssueCertificate(*csr, 1, FarFuture)
test.AssertNotError(t, err, "Failed to gracefully handle a CSR with duplicate names") 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 ca.MaxKeySize = 4096
// Test that the CA rejects CSRs that would expire after the intermediate cert // 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) csr, _ := x509.ParseCertificateRequest(csrDER)
_, err = ca.IssueCertificate(*csr, 1, FarPast) _, err = ca.IssueCertificate(*csr, 1, FarPast)
test.Assert(t, err == nil, "Can issue a certificate that expires after the underlying authorization.") 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 // 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) csr, _ = x509.ParseCertificateRequest(csrDER)
ca.NotAfter = time.Now() ca.NotAfter = time.Now()
_, err = ca.IssueCertificate(*csr, 1, FarFuture) _, err = ca.IssueCertificate(*csr, 1, FarFuture)

View File

@ -22,6 +22,7 @@ import (
blog "github.com/letsencrypt/boulder/log" blog "github.com/letsencrypt/boulder/log"
) )
// Constants for AMQP
const ( const (
QueueName = "Monitor" QueueName = "Monitor"
AmqpExchange = "boulder" AmqpExchange = "boulder"
@ -37,17 +38,17 @@ const (
AmqpImmediate = false AmqpImmediate = false
) )
var openCalls int64 = 0 var openCalls int64
func timeDelivery(d amqp.Delivery, stats statsd.Statter, deliveryTimings map[string]time.Time) { 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 // 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 // response then get time.Since original call from deliveryTiming, send timing metric, and
// decrement openCalls, in both cases send the gauges RpcCallsOpen and RpcBodySize // decrement openCalls, in both cases send the gauges RpcCallsOpen and RpcBodySize
if d.ReplyTo != "" { if d.ReplyTo != "" {
openCalls += 1 openCalls++
deliveryTimings[fmt.Sprintf("%s:%s", d.CorrelationId, d.ReplyTo)] = time.Now() deliveryTimings[fmt.Sprintf("%s:%s", d.CorrelationId, d.ReplyTo)] = time.Now()
} else { } else {
openCalls -= 1 openCalls--
rpcSent := deliveryTimings[fmt.Sprintf("%s:%s", d.CorrelationId, d.RoutingKey)] rpcSent := deliveryTimings[fmt.Sprintf("%s:%s", d.CorrelationId, d.RoutingKey)]
if rpcSent != *new(time.Time) { if rpcSent != *new(time.Time) {
respTime := time.Since(rpcSent) respTime := time.Since(rpcSent)

View File

@ -31,7 +31,7 @@ import (
gorp "github.com/letsencrypt/boulder/Godeps/_workspace/src/gopkg.in/gorp.v1" 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", 0: "unspecified",
1: "keyCompromise", 1: "keyCompromise",
2: "cACompromise", 2: "cACompromise",
@ -69,7 +69,7 @@ func setupContext(context *cli.Context) (rpc.CertificateAuthorityClient, *blog.A
ch := cmd.AmqpChannel(c.AMQP.Server) 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") cmd.FailOnError(err, "Unable to create RPC client")
cac, err := rpc.NewCertificateAuthorityClient(caRPC) cac, err := rpc.NewCertificateAuthorityClient(caRPC)
@ -147,7 +147,7 @@ func revokeByReg(regID int, reasonCode int, deny bool, cac rpc.CertificateAuthor
return return
} }
var version string = "0.0.1" var version = "0.0.1"
func main() { func main() {
app := cli.NewApp() app := cli.NewApp()
@ -231,7 +231,7 @@ func main() {
Usage: "List all revocation reason codes", Usage: "List all revocation reason codes",
Action: func(c *cli.Context) { Action: func(c *cli.Context) {
var codes []int var codes []int
for k, _ := range reasons { for k := range reasons {
codes = append(codes, k) codes = append(codes, k)
} }
sort.Ints(codes) sort.Ints(codes)

View File

@ -48,7 +48,7 @@ func main() {
ch := cmd.AmqpChannel(c.AMQP.Server) ch := cmd.AmqpChannel(c.AMQP.Server)
closeChan := ch.NotifyClose(make(chan *amqp.Error, 1)) 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") cmd.FailOnError(err, "Unable to create RPC client")
sac, err := rpc.NewStorageAuthorityClient(saRPC) sac, err := rpc.NewStorageAuthorityClient(saRPC)

View File

@ -41,13 +41,13 @@ func main() {
ch := cmd.AmqpChannel(c.AMQP.Server) ch := cmd.AmqpChannel(c.AMQP.Server)
closeChan := ch.NotifyClose(make(chan *amqp.Error, 1)) 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") 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") 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") cmd.FailOnError(err, "Unable to create RPC client")
vac, err := rpc.NewValidationAuthorityClient(vaRPC) vac, err := rpc.NewValidationAuthorityClient(vaRPC)

View File

@ -44,7 +44,7 @@ func main() {
ch := cmd.AmqpChannel(c.AMQP.Server) ch := cmd.AmqpChannel(c.AMQP.Server)
closeChan := ch.NotifyClose(make(chan *amqp.Error, 1)) 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") cmd.FailOnError(err, "Unable to create RPC client")
rac, err := rpc.NewRegistrationAuthorityClient(raRPC) rac, err := rpc.NewRegistrationAuthorityClient(raRPC)

View File

@ -23,10 +23,10 @@ func setupWFE(c cmd.Config) (rpc.RegistrationAuthorityClient, rpc.StorageAuthori
ch := cmd.AmqpChannel(c.AMQP.Server) ch := cmd.AmqpChannel(c.AMQP.Server)
closeChan := ch.NotifyClose(make(chan *amqp.Error, 1)) 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") 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") cmd.FailOnError(err, "Unable to create RPC client")
rac, err := rpc.NewRegistrationAuthorityClient(raRPC) rac, err := rpc.NewRegistrationAuthorityClient(raRPC)
@ -43,17 +43,18 @@ type timedHandler struct {
stats statsd.Statter 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 { func HandlerTimer(handler http.Handler, stats statsd.Statter) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
cStart := time.Now() cStart := time.Now()
openConnections += 1 openConnections++
stats.Gauge("HttpConnectionsOpen", openConnections, 1.0) stats.Gauge("HttpConnectionsOpen", openConnections, 1.0)
handler.ServeHTTP(w, r) handler.ServeHTTP(w, r)
openConnections -= 1 openConnections--
stats.Gauge("HttpConnectionsOpen", openConnections, 1.0) stats.Gauge("HttpConnectionsOpen", openConnections, 1.0)
// (FIX: this doesn't seem to really work at catching errors...) // (FIX: this doesn't seem to really work at catching errors...)

View File

@ -28,17 +28,18 @@ type timedHandler struct {
stats statsd.Statter 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 { func HandlerTimer(handler http.Handler, stats statsd.Statter) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
cStart := time.Now() cStart := time.Now()
openConnections += 1 openConnections++
stats.Gauge("HttpConnectionsOpen", openConnections, 1.0) stats.Gauge("HttpConnectionsOpen", openConnections, 1.0)
handler.ServeHTTP(w, r) handler.ServeHTTP(w, r)
openConnections -= 1 openConnections--
stats.Gauge("HttpConnectionsOpen", openConnections, 1.0) stats.Gauge("HttpConnectionsOpen", openConnections, 1.0)
// (FIX: this doesn't seem to really work at catching errors...) // (FIX: this doesn't seem to really work at catching errors...)

View File

@ -22,6 +22,7 @@ var pin = flag.String("pkcs11-pin", "", "PKCS#11 password")
var token = flag.String("pkcs11-token", "", "PKCS#11 token name") var token = flag.String("pkcs11-token", "", "PKCS#11 token name")
var label = flag.String("pkcs11-label", "", "PKCS#11 key label") var label = flag.String("pkcs11-label", "", "PKCS#11 key label")
// Config defines the configuration loaded from listFile.
type Config struct { type Config struct {
ThisUpdate time.Time ThisUpdate time.Time
NextUpdate time.Time NextUpdate time.Time

View File

@ -23,6 +23,7 @@ var pin = flag.String("pkcs11-pin", "", "PKCS#11 password")
var token = flag.String("pkcs11-token", "", "PKCS#11 token name") var token = flag.String("pkcs11-token", "", "PKCS#11 token name")
var label = flag.String("pkcs11-label", "", "PKCS#11 key label") var label = flag.String("pkcs11-label", "", "PKCS#11 key label")
// Config defines the configuration loaded from configFile.
type Config struct { type Config struct {
Name struct { Name struct {
C string C string

View File

@ -29,17 +29,18 @@ type timedHandler struct {
stats statsd.Statter 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 { func HandlerTimer(handler http.Handler, stats statsd.Statter) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
cStart := time.Now() cStart := time.Now()
openConnections += 1 openConnections++
stats.Gauge("HttpConnectionsOpen", openConnections, 1.0) stats.Gauge("HttpConnectionsOpen", openConnections, 1.0)
handler.ServeHTTP(w, r) handler.ServeHTTP(w, r)
openConnections -= 1 openConnections--
stats.Gauge("HttpConnectionsOpen", openConnections, 1.0) stats.Gauge("HttpConnectionsOpen", openConnections, 1.0)
// (FIX: this doesn't seem to really work at catching errors...) // (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, We assume that OCSP responses are stored in a very simple database table,
with two columns: serialNumber and response with two columns: serialNumber and response
@ -73,11 +76,14 @@ type DBSource struct {
caKeyHash []byte 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) { func NewSourceFromDatabase(dbMap *gorp.DbMap, caKeyHash []byte) (src *DBSource, err error) {
src = &DBSource{dbMap: dbMap, caKeyHash: caKeyHash} src = &DBSource{dbMap: dbMap, caKeyHash: caKeyHash}
return 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) { func (src *DBSource) Response(req *ocsp.Request) (response []byte, present bool) {
log := blog.GetAuditLogger() log := blog.GetAuditLogger()

View File

@ -30,7 +30,7 @@ func setupClients(c cmd.Config) (rpc.CertificateAuthorityClient, chan *amqp.Erro
ch := cmd.AmqpChannel(c.AMQP.Server) ch := cmd.AmqpChannel(c.AMQP.Server)
closeChan := ch.NotifyClose(make(chan *amqp.Error, 1)) 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") cmd.FailOnError(err, "Unable to create RPC client")
cac, err := rpc.NewCertificateAuthorityClient(caRPC) cac, err := rpc.NewCertificateAuthorityClient(caRPC)
@ -127,12 +127,12 @@ func findStaleResponses(cac rpc.CertificateAuthorityClient, dbMap *gorp.DbMap, o
log.Err(fmt.Sprintf("Could not process OCSP Response for %s: %s", status.Serial, err)) log.Err(fmt.Sprintf("Could not process OCSP Response for %s: %s", status.Serial, err))
tx.Rollback() tx.Rollback()
return err return err
} else { }
log.Info(fmt.Sprintf("OCSP %d: %s OK", i, status.Serial)) log.Info(fmt.Sprintf("OCSP %d: %s OK", i, status.Serial))
tx.Commit() tx.Commit()
} }
} }
}
return err return err
} }

View File

@ -227,6 +227,7 @@ func RunUntilSignaled(logger *blog.AuditLogger, server *rpc.AmqpRPCServer, close
logger.Warning("Reconnecting to AMQP...") logger.Warning("Reconnecting to AMQP...")
} }
// ProfileCmd runs forever, sending Go statistics to StatsD.
func ProfileCmd(profileName string, stats statsd.Statter) { func ProfileCmd(profileName string, stats statsd.Statter) {
for { for {
var memoryStats runtime.MemStats 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) { func LoadCert(path string) (cert []byte, err error) {
if path == "" { if path == "" {
err = errors.New("Issuer certificate was not provided in config.") err = errors.New("Issuer certificate was not provided in config.")

View File

@ -11,6 +11,7 @@ import (
blog "github.com/letsencrypt/boulder/log" blog "github.com/letsencrypt/boulder/log"
) )
// SimpleHTTPChallenge constructs a random HTTP challenge
func SimpleHTTPChallenge() Challenge { func SimpleHTTPChallenge() Challenge {
tls := true tls := true
return Challenge{ return Challenge{
@ -21,6 +22,7 @@ func SimpleHTTPChallenge() Challenge {
} }
} }
// DvsniChallenge constructs a random DVSNI challenge
func DvsniChallenge() Challenge { func DvsniChallenge() Challenge {
nonce := make([]byte, 16) nonce := make([]byte, 16)
_, err := rand.Read(nonce) _, err := rand.Read(nonce)
@ -39,6 +41,7 @@ func DvsniChallenge() Challenge {
} }
} }
// DNSChallenge constructs a random DNS challenge
func DNSChallenge() Challenge { func DNSChallenge() Challenge {
return Challenge{ return Challenge{
Type: ChallengeTypeDNS, Type: ChallengeTypeDNS,

View File

@ -41,16 +41,16 @@ func NewDNSResolver(dialTimeout time.Duration, servers []string) *DNSResolver {
// ExchangeOne performs a single DNS exchange with a randomly chosen server // ExchangeOne performs a single DNS exchange with a randomly chosen server
// out of the server list, returning the response, time, and error (if any) // 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) { func (dnsResolver *DNSResolver) ExchangeOne(m *dns.Msg) (rsp *dns.Msg, rtt time.Duration, err error) {
if len(r.Servers) < 1 { if len(dnsResolver.Servers) < 1 {
err = fmt.Errorf("Not configured with at least one DNS Server") err = fmt.Errorf("Not configured with at least one DNS Server")
return return
} }
// Randomly pick a server // 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 // LookupDNSSEC sends the provided DNS message to a randomly chosen server (see

View File

@ -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) { func GoodKeyECDSA(key ecdsa.PublicKey, maxKeySize int) (err error) {
log := blog.GetAuditLogger() log := blog.GetAuditLogger()
err = fmt.Errorf("ECDSA keys not yet supported") err = fmt.Errorf("ECDSA keys not yet supported")
@ -67,6 +68,7 @@ func GoodKeyECDSA(key ecdsa.PublicKey, maxKeySize int) (err error) {
return return
} }
// GoodKeyRSA determines if a RSA pubkey meets our requirements
func GoodKeyRSA(key rsa.PublicKey, maxKeySize int) (err error) { func GoodKeyRSA(key rsa.PublicKey, maxKeySize int) (err error) {
log := blog.GetAuditLogger() log := blog.GetAuditLogger()
// Baseline Requirements Appendix A // Baseline Requirements Appendix A

View File

@ -15,7 +15,7 @@ import (
"github.com/letsencrypt/boulder/test" "github.com/letsencrypt/boulder/test"
) )
var maxKeySize int = 2048 var maxKeySize = 2048
func TestUnknownKeyType(t *testing.T) { func TestUnknownKeyType(t *testing.T) {
notAKey := struct{}{} notAKey := struct{}{}

View File

@ -51,6 +51,7 @@ type WebFrontEnd interface {
Cert(response http.ResponseWriter, request *http.Request) Cert(response http.ResponseWriter, request *http.Request)
} }
// RegistrationAuthority defines the public interface for the Boulder RA
type RegistrationAuthority interface { type RegistrationAuthority interface {
// [WebFrontEnd] // [WebFrontEnd]
NewRegistration(Registration) (Registration, error) NewRegistration(Registration) (Registration, error)
@ -74,12 +75,14 @@ type RegistrationAuthority interface {
OnValidationUpdate(Authorization) error OnValidationUpdate(Authorization) error
} }
// ValidationAuthority defines the public interface for the Boulder VA
type ValidationAuthority interface { type ValidationAuthority interface {
// [RegistrationAuthority] // [RegistrationAuthority]
UpdateValidations(Authorization, int) error UpdateValidations(Authorization, int) error
CheckCAARecords(AcmeIdentifier) (bool, bool, error) CheckCAARecords(AcmeIdentifier) (bool, bool, error)
} }
// CertificateAuthority defines the public interface for the Boulder CA
type CertificateAuthority interface { type CertificateAuthority interface {
// [RegistrationAuthority] // [RegistrationAuthority]
IssueCertificate(x509.CertificateRequest, int64, time.Time) (Certificate, error) IssueCertificate(x509.CertificateRequest, int64, time.Time) (Certificate, error)
@ -87,11 +90,13 @@ type CertificateAuthority interface {
GenerateOCSP(OCSPSigningRequest) ([]byte, error) GenerateOCSP(OCSPSigningRequest) ([]byte, error)
} }
// PolicyAuthority defines the public interface for the Boulder PA
type PolicyAuthority interface { type PolicyAuthority interface {
WillingToIssue(AcmeIdentifier) error WillingToIssue(AcmeIdentifier) error
ChallengesFor(AcmeIdentifier) ([]Challenge, [][]int) ChallengesFor(AcmeIdentifier) ([]Challenge, [][]int)
} }
// StorageGetter are the Boulder SA's read-only methods
type StorageGetter interface { type StorageGetter interface {
GetRegistration(int64) (Registration, error) GetRegistration(int64) (Registration, error)
GetRegistrationByKey(jose.JsonWebKey) (Registration, error) GetRegistrationByKey(jose.JsonWebKey) (Registration, error)
@ -102,6 +107,7 @@ type StorageGetter interface {
AlreadyDeniedCSR([]string) (bool, error) AlreadyDeniedCSR([]string) (bool, error)
} }
// StorageAdder are the Boulder SA's write/update methods
type StorageAdder interface { type StorageAdder interface {
NewRegistration(Registration) (Registration, error) NewRegistration(Registration) (Registration, error)
UpdateRegistration(Registration) error UpdateRegistration(Registration) error

View File

@ -2,6 +2,7 @@
// This Source Code Form is subject to the terms of the Mozilla Public // 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 // 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/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
package core package core
import ( import (
@ -11,8 +12,11 @@ import (
"math/big" "math/big"
) )
// MaxUsed defines the maximum number of Nonces we're willing to hold in
// memory.
const MaxUsed = 65536 const MaxUsed = 65536
// NonceService generates, cancels, and tracks Nonces.
type NonceService struct { type NonceService struct {
latest int64 latest int64
earliest int64 earliest int64
@ -21,6 +25,7 @@ type NonceService struct {
maxUsed int maxUsed int
} }
// NewNonceService constructs a NonceService with defaults
func NewNonceService() NonceService { func NewNonceService() NonceService {
// XXX ignoring possible error due to entropy starvation // XXX ignoring possible error due to entropy starvation
key := make([]byte, 16) key := make([]byte, 16)
@ -44,7 +49,7 @@ func (ns NonceService) encrypt(counter int64) string {
// Generate a nonce with upper 4 bytes zero // Generate a nonce with upper 4 bytes zero
// XXX ignoring possible error due to entropy starvation // XXX ignoring possible error due to entropy starvation
nonce := make([]byte, 12) nonce := make([]byte, 12)
for i := 0; i < 4; i += 1 { for i := 0; i < 4; i++ {
nonce[i] = 0 nonce[i] = 0
} }
rand.Read(nonce[4:]) rand.Read(nonce[4:])
@ -70,7 +75,7 @@ func (ns NonceService) decrypt(nonce string) (int64, error) {
} }
n := make([]byte, 12) n := make([]byte, 12)
for i := 0; i < 4; i += 1 { for i := 0; i < 4; i++ {
n[i] = 0 n[i] = 0
} }
copy(n[4:], decoded[:8]) copy(n[4:], decoded[:8])
@ -85,8 +90,9 @@ func (ns NonceService) decrypt(nonce string) (int64, error) {
return ctr.Int64(), nil return ctr.Int64(), nil
} }
// Nonce provides a new Nonce.
func (ns *NonceService) Nonce() string { func (ns *NonceService) Nonce() string {
ns.latest += 1 ns.latest++
return ns.encrypt(ns.latest) return ns.encrypt(ns.latest)
} }
@ -100,6 +106,8 @@ func (ns *NonceService) minUsed() int64 {
return min return min
} }
// Valid determines whether the provided Nonce string is valid, returning
// true if so.
func (ns *NonceService) Valid(nonce string) bool { func (ns *NonceService) Valid(nonce string) bool {
c, err := ns.decrypt(nonce) c, err := ns.decrypt(nonce)
if err != nil { if err != nil {

View File

@ -18,11 +18,19 @@ import (
"time" "time"
) )
// IdentifierType defines the available identification mechanisms for domains
type IdentifierType string type IdentifierType string
// AcmeStatus defines the state of a given authorization
type AcmeStatus string type AcmeStatus string
// OCSPStatus defines the state of OCSP for a domain
type OCSPStatus string type OCSPStatus string
// Buffer is a variable-length collection of bytes
type Buffer []byte type Buffer []byte
// These statuses are the states of authorizations
const ( const (
StatusUnknown = AcmeStatus("unknown") // Unknown status; the default StatusUnknown = AcmeStatus("unknown") // Unknown status; the default
StatusPending = AcmeStatus("pending") // In process; client has next action StatusPending = AcmeStatus("pending") // In process; client has next action
@ -32,11 +40,13 @@ const (
StatusRevoked = AcmeStatus("revoked") // Object no longer valid StatusRevoked = AcmeStatus("revoked") // Object no longer valid
) )
// These status are the states of OCSP
const ( const (
OCSPStatusGood = OCSPStatus("good") OCSPStatusGood = OCSPStatus("good")
OCSPStatusRevoked = OCSPStatus("revoked") OCSPStatusRevoked = OCSPStatus("revoked")
) )
// These types are the available challenges
const ( const (
ChallengeTypeSimpleHTTP = "simpleHttp" ChallengeTypeSimpleHTTP = "simpleHttp"
ChallengeTypeDVSNI = "dvsni" ChallengeTypeDVSNI = "dvsni"
@ -44,6 +54,7 @@ const (
ChallengeTypeRecoveryToken = "recoveryToken" ChallengeTypeRecoveryToken = "recoveryToken"
) )
// These types are the available identification mechanisms
const ( const (
IdentifierDNS = IdentifierType("dns") IdentifierDNS = IdentifierType("dns")
) )
@ -88,7 +99,7 @@ type AcmeIdentifier struct {
Value string `json:"value"` // The identifier itself 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 // URIs pointing to authorizations that should collectively
// authorize the certificate being requsted. // authorize the certificate being requsted.
// //
@ -102,10 +113,11 @@ type CertificateRequest struct {
} }
type rawCertificateRequest struct { type rawCertificateRequest struct {
CSR JsonBuffer `json:"csr"` // The encoded CSR CSR JSONBuffer `json:"csr"` // The encoded CSR
Authorizations []AcmeURL `json:"authorizations"` // Authorizations Authorizations []AcmeURL `json:"authorizations"` // Authorizations
} }
// UnmarshalJSON provides an implementation for decoding CertificateRequest objects.
func (cr *CertificateRequest) UnmarshalJSON(data []byte) error { func (cr *CertificateRequest) UnmarshalJSON(data []byte) error {
var raw rawCertificateRequest var raw rawCertificateRequest
if err := json.Unmarshal(data, &raw); err != nil { if err := json.Unmarshal(data, &raw); err != nil {
@ -122,6 +134,7 @@ func (cr *CertificateRequest) UnmarshalJSON(data []byte) error {
return nil return nil
} }
// MarshalJSON provides an implementation for encoding CertificateRequest objects.
func (cr CertificateRequest) MarshalJSON() ([]byte, error) { func (cr CertificateRequest) MarshalJSON() ([]byte, error) {
return json.Marshal(rawCertificateRequest{ return json.Marshal(rawCertificateRequest{
CSR: cr.CSR.Raw, CSR: cr.CSR.Raw,
@ -150,6 +163,8 @@ type Registration struct {
LockCol int64 `json:"-"` LockCol int64 `json:"-"`
} }
// MergeUpdate copies a subset of information from the input Registration
// into this one.
func (r *Registration) MergeUpdate(input Registration) { func (r *Registration) MergeUpdate(input Registration) {
if len(input.Contact) > 0 { if len(input.Contact) > 0 {
r.Contact = input.Contact 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 // Rather than define individual types for different types of
// challenge, we just throw all the elements into one bucket, // challenge, we just throw all the elements into one bucket,
// together with the common metadata elements. // together with the common metadata elements.
@ -190,8 +207,8 @@ type Challenge struct {
Nonce string `json:"nonce,omitempty"` Nonce string `json:"nonce,omitempty"`
} }
// Check the sanity of a challenge object before issued to the client (completed = false) // IsSane checks the sanity of a challenge object before issued to the client
// and before validation (completed = true). // (completed = false) and before validation (completed = true).
func (ch Challenge) IsSane(completed bool) bool { func (ch Challenge) IsSane(completed bool) bool {
if ch.Status != StatusPending { if ch.Status != StatusPending {
return false return false
@ -211,8 +228,8 @@ func (ch Challenge) IsSane(completed bool) bool {
return false return false
} }
// Composed path should be a clean filepath (i.e. no double slashes, dot segments, etc) // 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) vaURL := fmt.Sprintf("/.well-known/acme-challenge/%s", ch.Path)
if vaUrl != filepath.Clean(vaUrl) { if vaURL != filepath.Clean(vaURL) {
return false return false
} }
} else { } else {
@ -286,7 +303,7 @@ func (ch Challenge) IsSane(completed bool) bool {
return true 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 '.' // Note: This method does not update the challenge on the left side of the '.'
func (ch Challenge) MergeResponse(resp Challenge) Challenge { func (ch Challenge) MergeResponse(resp Challenge) Challenge {
// Only override fields that are supposed to be client-provided // Only override fields that are supposed to be client-provided
@ -305,11 +322,10 @@ func (ch Challenge) MergeResponse(resp Challenge) Challenge {
return ch return ch
} }
// An ACME authorization object represents the authorization // Authorization represents the authorization of an account key holder
// of an account key holder to act on behalf of a domain. This // to act on behalf of a domain. This struct is intended to be used both
// struct is intended to be used both internally and for JSON // internally and for JSON marshaling on the wire. Any fields that should be
// marshaling on the wire. Any fields that should be suppressed // suppressed on the wire (e.g., ID, regID) must be made empty before marshaling.
// on the wire (e.g., ID, regID) must be made empty before marshaling.
type Authorization struct { type Authorization struct {
// An identifier for this authorization, unique across // An identifier for this authorization, unique across
// authorizations and certificates within this instance. // authorizations and certificates within this instance.
@ -340,28 +356,30 @@ type Authorization struct {
Combinations [][]int `json:"combinations,omitempty" db:"combinations"` 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. // 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 { func base64URLEncode(data []byte) string {
var result = base64.URLEncoding.EncodeToString(data) var result = base64.URLEncoding.EncodeToString(data)
return strings.TrimRight(result, "=") return strings.TrimRight(result, "=")
} }
// Url-safe base64 decoder that adds padding // URL-safe base64 decoder that adds padding
func base64URLDecode(data string) ([]byte, error) { func base64URLDecode(data string) ([]byte, error) {
var missing = (4 - len(data)%4) % 4 var missing = (4 - len(data)%4) % 4
data += strings.Repeat("=", missing) data += strings.Repeat("=", missing)
return base64.URLEncoding.DecodeString(data) 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)) 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 var str string
err = json.Unmarshal(data, &str) err = json.Unmarshal(data, &str)
if err != nil { if err != nil {
@ -383,14 +401,14 @@ type Certificate struct {
Serial string `db:"serial"` Serial string `db:"serial"`
Digest string `db:"digest"` Digest string `db:"digest"`
DER JsonBuffer `db:"der"` DER JSONBuffer `db:"der"`
Issued time.Time `db:"issued"` Issued time.Time `db:"issued"`
Expires time.Time `db:"expires"` Expires time.Time `db:"expires"`
} }
// Certificate.MatchesCSR tests the contents of a generated certificate to // MatchesCSR tests the contents of a generated certificate to make sure
// make sure that the PublicKey, CommonName, and DNSNames match those provided // that the PublicKey, CommonName, and DNSNames match those provided in
// in the CSR that was used to generate the certificate. It also checks the // the CSR that was used to generate the certificate. It also checks the
// following fields for: // following fields for:
// * notAfter is after earliestExpiry // * notAfter is after earliestExpiry
// * notBefore is not more than 24 hours ago // * notBefore is not more than 24 hours ago
@ -487,9 +505,10 @@ type CertificateStatus struct {
LockCol int64 `json:"-"` LockCol int64 `json:"-"`
} }
// A large table of OCSP responses. This contains all historical OCSP // OCSPResponse is a (large) table of OCSP responses. This contains all
// responses we've signed, is append-only, and is likely to get quite // historical OCSP responses we've signed, is append-only, and is likely to get
// large. We'll probably want administratively truncate it at some point. // quite large.
// It must be administratively truncated outside of Boulder.
type OCSPResponse struct { type OCSPResponse struct {
ID int `db:"id"` ID int `db:"id"`
@ -503,8 +522,9 @@ type OCSPResponse struct {
Response []byte `db:"response"` 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. // we've signed, is append-only, and is likely to get quite large.
// It must be administratively truncated outside of Boulder.
type CRL struct { type CRL struct {
// serial: Same as certificate serial. // serial: Same as certificate serial.
Serial string `db:"serial"` Serial string `db:"serial"`
@ -516,6 +536,7 @@ type CRL struct {
CRL string `db:"crl"` CRL string `db:"crl"`
} }
// DeniedCSR is a list of names we deny issuing.
type DeniedCSR struct { type DeniedCSR struct {
ID int `db:"id"` ID int `db:"id"`

View File

@ -13,7 +13,7 @@ import (
"github.com/letsencrypt/boulder/test" "github.com/letsencrypt/boulder/test"
) )
func TestRegistrationUupdate(t *testing.T) { func TestRegistrationUpdate(t *testing.T) {
oldURL, _ := url.Parse("http://old.invalid") oldURL, _ := url.Parse("http://old.invalid")
newURL, _ := url.Parse("http://new.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") test.Assert(t, !chall.IsSane(true), "IsSane should be false")
} }
func TestJsonBufferUnmarshal(t *testing.T) { func TestJSONBufferUnmarshal(t *testing.T) {
testStruct := struct { testStruct := struct {
Buffer JsonBuffer Buffer JSONBuffer
}{} }{}
notValidBase64 := []byte(`{"Buffer":"!!!!"}`) notValidBase64 := []byte(`{"Buffer":"!!!!"}`)

View File

@ -49,12 +49,29 @@ var BuildTime string
// Consequently, you should only use this error when Boulder's internal // Consequently, you should only use this error when Boulder's internal
// constraints have been violated. // constraints have been violated.
type InternalServerError string type InternalServerError string
// NotSupportedError indicates a method is not yet supported
type NotSupportedError string type NotSupportedError string
// MalformedRequestError indicates the user data was improper
type MalformedRequestError string type MalformedRequestError string
// UnauthorizedError indicates the user did not satisfactorily prove identity
type UnauthorizedError string type UnauthorizedError string
// NotFoundError indicates the destination was unknown. Whoa oh oh ohhh.
type NotFoundError string type NotFoundError string
// SyntaxError indicates the user improperly formatted their data.
type SyntaxError string 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 type SignatureValidationError string
// CertificateIssuanceError indicates the certificate failed to be issued
// for some reason.
type CertificateIssuanceError string type CertificateIssuanceError string
func (e InternalServerError) Error() string { return string(e) } func (e InternalServerError) Error() string { return string(e) }
@ -82,10 +99,12 @@ func unpad(x string) string {
return strings.Replace(x, "=", "", -1) return strings.Replace(x, "=", "", -1)
} }
// B64enc encodes a byte array as unpadded, URL-safe Base64
func B64enc(x []byte) string { func B64enc(x []byte) string {
return unpad(base64.URLEncoding.EncodeToString(x)) return unpad(base64.URLEncoding.EncodeToString(x))
} }
// B64dec decodes a byte array from unpadded, URL-safe Base64
func B64dec(x string) ([]byte, error) { func B64dec(x string) ([]byte, error) {
return base64.URLEncoding.DecodeString(pad(x)) return base64.URLEncoding.DecodeString(pad(x))
} }
@ -104,18 +123,23 @@ func RandomString(byteLength int) string {
return B64enc(b) return B64enc(b)
} }
// NewToken produces a random string for Challenges, etc.
func NewToken() string { func NewToken() string {
return RandomString(32) return RandomString(32)
} }
// Fingerprints // Fingerprints
// Fingerprint256 produces an unpadded, URL-safe Base64-encoded SHA256 digest
// of the data.
func Fingerprint256(data []byte) string { func Fingerprint256(data []byte) string {
d := sha256.New() d := sha256.New()
_, _ = d.Write(data) // Never returns an error _, _ = d.Write(data) // Never returns an error
return B64enc(d.Sum(nil)) 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) { func KeyDigest(key crypto.PublicKey) (string, error) {
switch t := key.(type) { switch t := key.(type) {
case *jose.JsonWebKey: 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 { func KeyDigestEquals(j, k crypto.PublicKey) bool {
jDigest, jErr := KeyDigest(j) digestJ, errJ := KeyDigest(j)
kDigest, kErr := KeyDigest(k) digestK, errK := KeyDigest(k)
// Keys that don't have a valid digest (due to marshalling problems) // Keys that don't have a valid digest (due to marshalling problems)
// are never equal. So, e.g. nil keys are not equal. // are never equal. So, e.g. nil keys are not equal.
if jErr != nil || kErr != nil { if errJ != nil || errK != nil {
return false 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 type AcmeURL url.URL
func (u AcmeURL) String() string { func (u AcmeURL) String() string {
@ -153,6 +178,7 @@ func (u AcmeURL) String() string {
return url.String() return url.String()
} }
// PathSegments splits an AcmeURL into segments on the '/' characters
func (u AcmeURL) PathSegments() (segments []string) { func (u AcmeURL) PathSegments() (segments []string) {
segments = strings.Split(u.Path, "/") segments = strings.Split(u.Path, "/")
if len(segments) > 0 && len(segments[0]) == 0 { if len(segments) > 0 && len(segments[0]) == 0 {
@ -161,11 +187,13 @@ func (u AcmeURL) PathSegments() (segments []string) {
return return
} }
// MarshalJSON encodes an AcmeURL for transfer
func (u AcmeURL) MarshalJSON() ([]byte, error) { func (u AcmeURL) MarshalJSON() ([]byte, error) {
uu := url.URL(u) uu := url.URL(u)
return json.Marshal(uu.String()) return json.Marshal(uu.String())
} }
// UnmarshalJSON decodes an AcmeURL from transfer
func (u *AcmeURL) UnmarshalJSON(data []byte) error { func (u *AcmeURL) UnmarshalJSON(data []byte) error {
var str string var str string
if err := json.Unmarshal(data, &str); err != nil { if err := json.Unmarshal(data, &str); err != nil {
@ -177,7 +205,9 @@ func (u *AcmeURL) UnmarshalJSON(data []byte) error {
return err 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 { func VerifyCSR(csr *x509.CertificateRequest) error {
// Compute the hash of the TBSCertificateRequest // Compute the hash of the TBSCertificateRequest
var hashID crypto.Hash var hashID crypto.Hash
@ -237,18 +267,22 @@ func VerifyCSR(csr *x509.CertificateRequest) error {
if ecdsa.Verify(ecKey, inputHash, sig.R, sig.S) { if ecdsa.Verify(ecKey, inputHash, sig.R, sig.S) {
return nil 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") 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 { func SerialToString(serial *big.Int) string {
return fmt.Sprintf("%032x", serial) return fmt.Sprintf("%032x", serial)
} }
// StringToSerial converts a string into a certificate serial number (big.Int)
// consistently.
func StringToSerial(serial string) (*big.Int, error) { func StringToSerial(serial string) (*big.Int, error) {
var serialNum big.Int var serialNum big.Int
if len(serial) != 32 { if len(serial) != 32 {
@ -285,6 +319,7 @@ func GetBuildHost() (retID string) {
return return
} }
// UniqueNames returns the set of all unique names in the input.
func UniqueNames(names []string) (unique []string) { func UniqueNames(names []string) (unique []string) {
nameMap := make(map[string]int, len(names)) nameMap := make(map[string]int, len(names))
for _, name := range names { for _, name := range names {

View File

@ -51,13 +51,13 @@ func TestBuildID(t *testing.T) {
test.AssertEquals(t, "Unspecified", GetBuildID()) test.AssertEquals(t, "Unspecified", GetBuildID())
} }
const JWK_1_JSON = `{ const JWK1JSON = `{
"kty": "RSA", "kty": "RSA",
"n": "vuc785P8lBj3fUxyZchF_uZw6WtbxcorqgTyq-qapF5lrO1U82Tp93rpXlmctj6fyFHBVVB5aXnUHJ7LZeVPod7Wnfl8p5OyhlHQHC8BnzdzCqCMKmWZNX5DtETDId0qzU7dPzh0LP0idt5buU7L9QNaabChw3nnaL47iu_1Di5Wp264p2TwACeedv2hfRDjDlJmaQXuS8Rtv9GnRWyC9JBu7XmGvGDziumnJH7Hyzh3VNu-kSPQD3vuAFgMZS6uUzOztCkT0fpOalZI6hqxtWLvXUMj-crXrn-Maavz8qRhpAyp5kcYk3jiHGgQIi7QSK2JIdRJ8APyX9HlmTN5AQ", "n": "vuc785P8lBj3fUxyZchF_uZw6WtbxcorqgTyq-qapF5lrO1U82Tp93rpXlmctj6fyFHBVVB5aXnUHJ7LZeVPod7Wnfl8p5OyhlHQHC8BnzdzCqCMKmWZNX5DtETDId0qzU7dPzh0LP0idt5buU7L9QNaabChw3nnaL47iu_1Di5Wp264p2TwACeedv2hfRDjDlJmaQXuS8Rtv9GnRWyC9JBu7XmGvGDziumnJH7Hyzh3VNu-kSPQD3vuAFgMZS6uUzOztCkT0fpOalZI6hqxtWLvXUMj-crXrn-Maavz8qRhpAyp5kcYk3jiHGgQIi7QSK2JIdRJ8APyX9HlmTN5AQ",
"e": "AAEAAQ" "e": "AAEAAQ"
}` }`
const JWK_1_DIGEST = `ul04Iq07ulKnnrebv2hv3yxCGgVvoHs8hjq2tVKx3mc=` const JWK1Digest = `ul04Iq07ulKnnrebv2hv3yxCGgVvoHs8hjq2tVKx3mc=`
const JWK_2_JSON = `{ const JWK2JSON = `{
"kty":"RSA", "kty":"RSA",
"n":"yTsLkI8n4lg9UuSKNRC0UPHsVjNdCYk8rGXIqeb_rRYaEev3D9-kxXY8HrYfGkVt5CiIVJ-n2t50BKT8oBEMuilmypSQqJw0pCgtUm-e6Z0Eg3Ly6DMXFlycyikegiZ0b-rVX7i5OCEZRDkENAYwFNX4G7NNCwEZcH7HUMUmty9dchAqDS9YWzPh_dde1A9oy9JMH07nRGDcOzIh1rCPwc71nwfPPYeeS4tTvkjanjeigOYBFkBLQuv7iBB4LPozsGF1XdoKiIIi-8ye44McdhOTPDcQp3xKxj89aO02pQhBECv61rmbPinvjMG9DYxJmZvjsKF4bN2oy0DxdC1jDw", "n":"yTsLkI8n4lg9UuSKNRC0UPHsVjNdCYk8rGXIqeb_rRYaEev3D9-kxXY8HrYfGkVt5CiIVJ-n2t50BKT8oBEMuilmypSQqJw0pCgtUm-e6Z0Eg3Ly6DMXFlycyikegiZ0b-rVX7i5OCEZRDkENAYwFNX4G7NNCwEZcH7HUMUmty9dchAqDS9YWzPh_dde1A9oy9JMH07nRGDcOzIh1rCPwc71nwfPPYeeS4tTvkjanjeigOYBFkBLQuv7iBB4LPozsGF1XdoKiIIi-8ye44McdhOTPDcQp3xKxj89aO02pQhBECv61rmbPinvjMG9DYxJmZvjsKF4bN2oy0DxdC1jDw",
"e":"AAEAAQ" "e":"AAEAAQ"
@ -66,13 +66,13 @@ const JWK_2_JSON = `{
func TestKeyDigest(t *testing.T) { func TestKeyDigest(t *testing.T) {
// Test with JWK (value, reference, and direct) // Test with JWK (value, reference, and direct)
var jwk jose.JsonWebKey var jwk jose.JsonWebKey
json.Unmarshal([]byte(JWK_1_JSON), &jwk) json.Unmarshal([]byte(JWK1JSON), &jwk)
digest, err := KeyDigest(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) 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) 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 // Test with unknown key type
digest, err = KeyDigest(struct{}{}) digest, err = KeyDigest(struct{}{})
@ -81,8 +81,8 @@ func TestKeyDigest(t *testing.T) {
func TestKeyDigestEquals(t *testing.T) { func TestKeyDigestEquals(t *testing.T) {
var jwk1, jwk2 jose.JsonWebKey var jwk1, jwk2 jose.JsonWebKey
json.Unmarshal([]byte(JWK_1_JSON), &jwk1) json.Unmarshal([]byte(JWK1JSON), &jwk1)
json.Unmarshal([]byte(JWK_2_JSON), &jwk2) json.Unmarshal([]byte(JWK2JSON), &jwk2)
test.Assert(t, KeyDigestEquals(jwk1, jwk1), "Key digests for same key should match") 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") test.Assert(t, !KeyDigestEquals(jwk1, jwk2), "Key digests for different keys should not match")

View File

@ -9,6 +9,7 @@ import (
"net/smtp" "net/smtp"
) )
// Mailer defines a mail transfer agent to use for sending mail
type Mailer struct { type Mailer struct {
Server string Server string
Port string Port string
@ -16,6 +17,8 @@ type Mailer struct {
From string From string
} }
// NewMailer constructs a Mailer to represent an account at a particular mail
// transfer agent.
func NewMailer(server, port, username, password string) Mailer { func NewMailer(server, port, username, password string) Mailer {
auth := smtp.PlainAuth("", username, password, server) auth := smtp.PlainAuth("", username, password, server)
return Mailer{ 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) { func (m *Mailer) SendMail(to []string, msg string) (err error) {
err = smtp.SendMail(m.Server+":"+m.Port, m.Auth, m.From, to, []byte(msg)) err = smtp.SendMail(m.Server+":"+m.Port, m.Auth, m.From, to, []byte(msg))
return return

View File

@ -6,7 +6,6 @@
package policy package policy
import ( import (
"errors"
"net" "net"
"regexp" "regexp"
"strings" "strings"
@ -15,6 +14,7 @@ import (
blog "github.com/letsencrypt/boulder/log" blog "github.com/letsencrypt/boulder/log"
) )
// PolicyAuthorityImpl enforces CA policy decisions.
type PolicyAuthorityImpl struct { type PolicyAuthorityImpl struct {
log *blog.AuditLogger log *blog.AuditLogger
@ -22,6 +22,7 @@ type PolicyAuthorityImpl struct {
Blacklist map[string]bool // A blacklist of denied names Blacklist map[string]bool // A blacklist of denied names
} }
// NewPolicyAuthorityImpl constructs a Policy Authority.
func NewPolicyAuthorityImpl() *PolicyAuthorityImpl { func NewPolicyAuthorityImpl() *PolicyAuthorityImpl {
logger := blog.GetAuditLogger() logger := blog.GetAuditLogger()
logger.Notice("Policy Authority Starting") 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 // 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). // have at least one label beyond any suffix in the set).
func suffixMatch(labels []string, suffixSet map[string]bool, properSuffix bool) bool { 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 domain := strings.Join(labels[i:], "."); suffixSet[domain] {
// If we match on the whole domain, gate on properSuffix // If we match on the whole domain, gate on properSuffix
return !properSuffix || (i > 0) return !properSuffix || (i > 0)
@ -61,11 +62,28 @@ func suffixMatch(labels []string, suffixSet map[string]bool, properSuffix bool)
return false return false
} }
var InvalidIdentifierError = errors.New("Invalid identifier type") // InvalidIdentifierError indicates that we didn't understand the IdentifierType
var SyntaxError = errors.New("Syntax error") // provided.
var NonPublicError = errors.New("Name does not end in a public suffix") type InvalidIdentifierError struct{}
var BlacklistedError = errors.New("Name is blacklisted")
// 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: // We place several criteria on identifiers we are willing to issue for:
// //
// * MUST self-identify as DNS identifiers // * 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. // XXX: We should probably fold everything to lower-case somehow.
func (pa PolicyAuthorityImpl) WillingToIssue(id core.AcmeIdentifier) error { func (pa PolicyAuthorityImpl) WillingToIssue(id core.AcmeIdentifier) error {
if id.Type != core.IdentifierDNS { if id.Type != core.IdentifierDNS {
return InvalidIdentifierError return InvalidIdentifierError{}
} }
domain := id.Value domain := id.Value
for _, ch := range []byte(domain) { for _, ch := range []byte(domain) {
if !isDNSCharacter(ch) { if !isDNSCharacter(ch) {
return SyntaxError return SyntaxError{}
} }
} }
domain = strings.ToLower(domain) domain = strings.ToLower(domain)
if len(domain) > 255 { if len(domain) > 255 {
return SyntaxError return SyntaxError{}
} }
if ip := net.ParseIP(domain); ip != nil { if ip := net.ParseIP(domain); ip != nil {
return SyntaxError return SyntaxError{}
} }
labels := strings.Split(domain, ".") labels := strings.Split(domain, ".")
if len(labels) > maxLabels || len(labels) < 2 { if len(labels) > maxLabels || len(labels) < 2 {
return SyntaxError return SyntaxError{}
} }
for _, label := range labels { for _, label := range labels {
// DNS defines max label length as 63 characters. Some implementations allow // DNS defines max label length as 63 characters. Some implementations allow
// more, but we will be conservative. // more, but we will be conservative.
if len(label) < 1 || len(label) > 63 { if len(label) < 1 || len(label) > 63 {
return SyntaxError return SyntaxError{}
} }
if !dnsLabelRegexp.MatchString(label) { if !dnsLabelRegexp.MatchString(label) {
return SyntaxError return SyntaxError{}
} }
if punycodeRegexp.MatchString(label) { if punycodeRegexp.MatchString(label) {
return SyntaxError return SyntaxError{}
} }
} }
// Require match to PSL, plus at least one label // Require match to PSL, plus at least one label
if !suffixMatch(labels, pa.PublicSuffixList, true) { if !suffixMatch(labels, pa.PublicSuffixList, true) {
return NonPublicError return NonPublicError{}
} }
// Require no match against blacklist // Require no match against blacklist
if suffixMatch(labels, pa.Blacklist, false) { if suffixMatch(labels, pa.Blacklist, false) {
return BlacklistedError return BlacklistedError{}
} }
return nil 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) { func (pa PolicyAuthorityImpl) ChallengesFor(identifier core.AcmeIdentifier) (challenges []core.Challenge, combinations [][]int) {
challenges = []core.Challenge{ challenges = []core.Challenge{
core.SimpleHTTPChallenge(), core.SimpleHTTPChallenge(),

View File

@ -92,14 +92,17 @@ func TestWillingToIssue(t *testing.T) {
// Test for invalid identifier type // Test for invalid identifier type
identifier := core.AcmeIdentifier{Type: "ip", Value: "example.com"} identifier := core.AcmeIdentifier{Type: "ip", Value: "example.com"}
err := pa.WillingToIssue(identifier) err := pa.WillingToIssue(identifier)
if err != InvalidIdentifierError { _, ok := err.(InvalidIdentifierError)
if !ok {
t.Error("Identifier was not correctly forbidden: ", identifier) t.Error("Identifier was not correctly forbidden: ", identifier)
} }
// Test syntax errors // Test syntax errors
for _, domain := range shouldBeSyntaxError { for _, domain := range shouldBeSyntaxError {
identifier := core.AcmeIdentifier{Type: core.IdentifierDNS, Value: domain} 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) t.Error("Identifier was not correctly forbidden: ", identifier, err)
} }
} }
@ -107,7 +110,9 @@ func TestWillingToIssue(t *testing.T) {
// Test public suffix matching // Test public suffix matching
for _, domain := range shouldBeNonPublic { for _, domain := range shouldBeNonPublic {
identifier := core.AcmeIdentifier{Type: core.IdentifierDNS, Value: domain} 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) t.Error("Identifier was not correctly forbidden: ", identifier, err)
} }
} }
@ -115,7 +120,9 @@ func TestWillingToIssue(t *testing.T) {
// Test blacklisting // Test blacklisting
for _, domain := range shouldBeBlacklisted { for _, domain := range shouldBeBlacklisted {
identifier := core.AcmeIdentifier{Type: core.IdentifierDNS, Value: domain} 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) t.Error("Identifier was not correctly forbidden: ", identifier, err)
} }
} }

View File

@ -23,7 +23,9 @@ import (
"github.com/letsencrypt/boulder/policy" "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. // populated, or there is a risk of panic.
type RegistrationAuthorityImpl struct { type RegistrationAuthorityImpl struct {
CA core.CertificateAuthority CA core.CertificateAuthority
@ -36,6 +38,7 @@ type RegistrationAuthorityImpl struct {
MaxKeySize int MaxKeySize int
} }
// NewRegistrationAuthorityImpl constructs a new RA object.
func NewRegistrationAuthorityImpl() RegistrationAuthorityImpl { func NewRegistrationAuthorityImpl() RegistrationAuthorityImpl {
logger := blog.GetAuditLogger() logger := blog.GetAuditLogger()
logger.Notice("Registration Authority Starting") logger.Notice("Registration Authority Starting")
@ -103,6 +106,7 @@ type certificateRequestEvent struct {
Error string `json:",omitempty"` Error string `json:",omitempty"`
} }
// NewRegistration constructs a new Registration from a request.
func (ra *RegistrationAuthorityImpl) NewRegistration(init core.Registration) (reg core.Registration, err error) { func (ra *RegistrationAuthorityImpl) NewRegistration(init core.Registration) (reg core.Registration, err error) {
if err = core.GoodKey(init.Key.Key, ra.MaxKeySize); err != nil { if err = core.GoodKey(init.Key.Key, ra.MaxKeySize); err != nil {
return core.Registration{}, core.MalformedRequestError(fmt.Sprintf("Invalid public key: %s", err.Error())) 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 return
} }
// NewAuthorization constuct a new Authz from a request.
func (ra *RegistrationAuthorityImpl) NewAuthorization(request core.Authorization, regID int64) (authz core.Authorization, err error) { func (ra *RegistrationAuthorityImpl) NewAuthorization(request core.Authorization, regID int64) (authz core.Authorization, err error) {
if regID <= 0 { if regID <= 0 {
err = core.MalformedRequestError(fmt.Sprintf("Invalid registration ID: %d", regID)) err = core.MalformedRequestError(fmt.Sprintf("Invalid registration ID: %d", regID))
@ -202,6 +207,7 @@ func (ra *RegistrationAuthorityImpl) NewAuthorization(request core.Authorization
return authz, err return authz, err
} }
// NewCertificate requests the issuance of a certificate.
func (ra *RegistrationAuthorityImpl) NewCertificate(req core.CertificateRequest, regID int64) (cert core.Certificate, err error) { func (ra *RegistrationAuthorityImpl) NewCertificate(req core.CertificateRequest, regID int64) (cert core.Certificate, err error) {
emptyCert := core.Certificate{} emptyCert := core.Certificate{}
var logEventResult string var logEventResult string
@ -306,7 +312,7 @@ func (ra *RegistrationAuthorityImpl) NewCertificate(req core.CertificateRequest,
authorizedDomains[authz.Identifier.Value] = true authorizedDomains[authz.Identifier.Value] = true
} }
verificationMethods := []string{} verificationMethods := []string{}
for method, _ := range verificationMethodSet { for method := range verificationMethodSet {
verificationMethods = append(verificationMethods, method) verificationMethods = append(verificationMethods, method)
} }
logEvent.VerificationMethods = verificationMethods logEvent.VerificationMethods = verificationMethods
@ -358,6 +364,7 @@ func (ra *RegistrationAuthorityImpl) NewCertificate(req core.CertificateRequest,
return cert, nil 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) { func (ra *RegistrationAuthorityImpl) UpdateRegistration(base core.Registration, update core.Registration) (reg core.Registration, err error) {
base.MergeUpdate(update) base.MergeUpdate(update)
@ -376,6 +383,7 @@ func (ra *RegistrationAuthorityImpl) UpdateRegistration(base core.Registration,
return 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) { 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 // Copy information over that the client is allowed to supply
authz = base authz = base
@ -399,6 +407,7 @@ func (ra *RegistrationAuthorityImpl) UpdateAuthorization(base core.Authorization
return return
} }
// RevokeCertificate terminates trust in the certificate provided.
func (ra *RegistrationAuthorityImpl) RevokeCertificate(cert x509.Certificate) (err error) { func (ra *RegistrationAuthorityImpl) RevokeCertificate(cert x509.Certificate) (err error) {
serialString := core.SerialToString(cert.SerialNumber) serialString := core.SerialToString(cert.SerialNumber)
err = ra.CA.RevokeCertificate(serialString, 0) err = ra.CA.RevokeCertificate(serialString, 0)
@ -413,6 +422,7 @@ func (ra *RegistrationAuthorityImpl) RevokeCertificate(cert x509.Certificate) (e
return err return err
} }
// OnValidationUpdate is called when a given Authorization is updated by the VA.
func (ra *RegistrationAuthorityImpl) OnValidationUpdate(authz core.Authorization) error { func (ra *RegistrationAuthorityImpl) OnValidationUpdate(authz core.Authorization) error {
// Consider validation successful if any of the combinations // Consider validation successful if any of the combinations
// specified in the authorization has been fulfilled // specified in the authorization has been fulfilled

View File

@ -154,9 +154,9 @@ func initAuthorities(t *testing.T) (core.CertificateAuthority, *DummyValidationA
va := &DummyValidationAuthority{} va := &DummyValidationAuthority{}
// PEM files in certificate-authority_test.go // PEM files in certificate-authority_test.go
caKeyPEM, _ := pem.Decode([]byte(CA_KEY_PEM)) caKeyPEM, _ := pem.Decode([]byte(CAkeyPEM))
caKey, _ := x509.ParsePKCS1PrivateKey(caKeyPEM.Bytes) caKey, _ := x509.ParsePKCS1PrivateKey(caKeyPEM.Bytes)
caCertPEM, _ := pem.Decode([]byte(CA_CERT_PEM)) caCertPEM, _ := pem.Decode([]byte(CAcertPEM))
caCert, _ := x509.ParseCertificate(caCertPEM.Bytes) caCert, _ := x509.ParseCertificate(caCertPEM.Bytes)
basicPolicy := &cfsslConfig.Signing{ basicPolicy := &cfsslConfig.Signing{
Default: &cfsslConfig.SigningProfile{ Default: &cfsslConfig.SigningProfile{
@ -184,7 +184,7 @@ func initAuthorities(t *testing.T) (core.CertificateAuthority, *DummyValidationA
NotAfter: time.Now().Add(time.Hour * 8761), NotAfter: time.Now().Add(time.Hour * 8761),
MaxKeySize: 4096, MaxKeySize: 4096,
} }
csrDER, _ := hex.DecodeString(CSR_HEX) csrDER, _ := hex.DecodeString(CSRhex)
ExampleCSR, _ = x509.ParseCertificateRequest(csrDER) ExampleCSR, _ = x509.ParseCertificateRequest(csrDER)
// This registration implicitly gets ID = 1 // This registration implicitly gets ID = 1
@ -373,8 +373,8 @@ func TestUpdateAuthorization(t *testing.T) {
// Verify that the responses are reflected // Verify that the responses are reflected
test.Assert(t, len(va.Argument.Challenges) > 0, "Authz passed to VA has no challenges") test.Assert(t, len(va.Argument.Challenges) > 0, "Authz passed to VA has no challenges")
simpleHttp := va.Argument.Challenges[0] simpleHTTP := va.Argument.Challenges[0]
test.Assert(t, simpleHttp.Path == Response.Path, "simpleHttp changed") test.Assert(t, simpleHTTP.Path == Response.Path, "simpleHTTP changed")
t.Log("DONE TestUpdateAuthorization") t.Log("DONE TestUpdateAuthorization")
} }
@ -503,7 +503,7 @@ func TestNewCertificate(t *testing.T) {
t.Log("DONE TestOnValidationUpdate") t.Log("DONE TestOnValidationUpdate")
} }
var CA_KEY_PEM = "-----BEGIN RSA PRIVATE KEY-----\n" + var CAkeyPEM = "-----BEGIN RSA PRIVATE KEY-----\n" +
"MIIJKQIBAAKCAgEAqmM0dEf/J9MCk2ItzevL0dKJ84lVUtf/vQ7AXFi492vFXc3b\n" + "MIIJKQIBAAKCAgEAqmM0dEf/J9MCk2ItzevL0dKJ84lVUtf/vQ7AXFi492vFXc3b\n" +
"PrJz2ybtjO08oVkhRrFGGgLufL2JeOBn5pUZQrp6TqyCLoQ4f/yrmu9tCeG8CtDg\n" + "PrJz2ybtjO08oVkhRrFGGgLufL2JeOBn5pUZQrp6TqyCLoQ4f/yrmu9tCeG8CtDg\n" +
"xi6Ye9LjvlchEHhUKhAHc8uL+ablHzWxHTeuhnuThrsLFUcJQWb10U27LiXp3XCW\n" + "xi6Ye9LjvlchEHhUKhAHc8uL+ablHzWxHTeuhnuThrsLFUcJQWb10U27LiXp3XCW\n" +
@ -555,7 +555,7 @@ var CA_KEY_PEM = "-----BEGIN RSA PRIVATE KEY-----\n" +
"huu1W6p9RdxJHgphzmGAvTrOmrDAZeKtubsMS69VZVFjQFa1ZD/VMzWK1X2o\n" + "huu1W6p9RdxJHgphzmGAvTrOmrDAZeKtubsMS69VZVFjQFa1ZD/VMzWK1X2o\n" +
"-----END RSA PRIVATE KEY-----" "-----END RSA PRIVATE KEY-----"
var CA_CERT_PEM = "-----BEGIN CERTIFICATE-----\n" + var CAcertPEM = "-----BEGIN CERTIFICATE-----\n" +
"MIIFxDCCA6ygAwIBAgIJALe2d/gZHJqAMA0GCSqGSIb3DQEBCwUAMDExCzAJBgNV\n" + "MIIFxDCCA6ygAwIBAgIJALe2d/gZHJqAMA0GCSqGSIb3DQEBCwUAMDExCzAJBgNV\n" +
"BAYTAlVTMRAwDgYDVQQKDAdUZXN0IENBMRAwDgYDVQQDDAdUZXN0IENBMB4XDTE1\n" + "BAYTAlVTMRAwDgYDVQQKDAdUZXN0IENBMRAwDgYDVQQDDAdUZXN0IENBMB4XDTE1\n" +
"MDIxMzAwMzI0NFoXDTI1MDIxMDAwMzI0NFowMTELMAkGA1UEBhMCVVMxEDAOBgNV\n" + "MDIxMzAwMzI0NFoXDTI1MDIxMDAwMzI0NFowMTELMAkGA1UEBhMCVVMxEDAOBgNV\n" +
@ -594,7 +594,7 @@ var CA_CERT_PEM = "-----BEGIN CERTIFICATE-----\n" +
// * CN = not-example.com // * CN = not-example.com
// * DNSNames = not-example.com, www.not-example.com // * DNSNames = not-example.com, www.not-example.com
/* /*
var CSR_HEX = "3082028c30820174020100301a311830160603550403130f" + var CSRhex = "3082028c30820174020100301a311830160603550403130f" +
"6e6f742d6578616d706c652e636f6d30820122300d06092a" + "6e6f742d6578616d706c652e636f6d30820122300d06092a" +
"864886f70d01010105000382010f003082010a0282010100" + "864886f70d01010105000382010f003082010a0282010100" +
"aac67dd1e11fae980048b0ac91be005f21d9df8bb38461cc" + "aac67dd1e11fae980048b0ac91be005f21d9df8bb38461cc" +
@ -624,7 +624,7 @@ var CSR_HEX = "3082028c30820174020100301a311830160603550403130f" +
"f277b6d23fa24f9b" "f277b6d23fa24f9b"
*/ */
var CSR_HEX = "308202ae308201960201003027310b300906035504061302" + var CSRhex = "308202ae308201960201003027310b300906035504061302" +
"5553311830160603550403130f6e6f742d6578616d706c65" + "5553311830160603550403130f6e6f742d6578616d706c65" +
"2e636f6d30820122300d06092a864886f70d010101050003" + "2e636f6d30820122300d06092a864886f70d010101050003" +
"82010f003082010a0282010100a4f507b52ca2766e2cea7b" + "82010f003082010a0282010100a4f507b52ca2766e2cea7b" +

View File

@ -107,7 +107,7 @@ func amqpSubscribe(ch *amqp.Channel, name string, log *blog.AuditLogger) (msgs <
return 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, // When messages arrive on that queue, it dispatches them based on type,
// and returns the response to the ReplyTo queue. // and returns the response to the ReplyTo queue.
// //
@ -120,7 +120,7 @@ type AmqpRPCServer struct {
dispatchTable map[string]func([]byte) ([]byte, error) 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 // Note that you must call Start() to actually start the server
// listening for requests. // listening for requests.
func NewAmqpRPCServer(serverQueue string, channel *amqp.Channel) *AmqpRPCServer { 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)) { func (rpc *AmqpRPCServer) Handle(method string, handler func([]byte) ([]byte, error)) {
rpc.dispatchTable[method] = handler 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{}. // due to type interface{}.
type RPCError struct { type RPCError struct {
Value string `json:"value"` Value string `json:"value"`
@ -198,12 +199,14 @@ func unwrapError(rpcError RPCError) (err error) {
return return
} }
// RPCResponse is a stuct for wire-representation of response messages
// used by DispatchSync
type RPCResponse struct { type RPCResponse struct {
ReturnVal []byte `json:"returnVal,omitempty"` ReturnVal []byte `json:"returnVal,omitempty"`
Error RPCError `json:"error,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. // There is currently no Stop() method.
func (rpc *AmqpRPCServer) Start() (err error) { func (rpc *AmqpRPCServer) Start() (err error) {
msgs, err := amqpSubscribe(rpc.channel, rpc.serverQueue, rpc.log) msgs, err := amqpSubscribe(rpc.channel, rpc.serverQueue, rpc.log)
@ -246,8 +249,8 @@ func (rpc *AmqpRPCServer) Start() (err error) {
return return
} }
// An AMQP-RPC client sends requests to a specific server queue, // AmqpRPCCLient is an AMQP-RPC client that sends requests to a specific server
// and uses a dedicated response queue for responses. // queue, and uses a dedicated response queue for responses.
// //
// To implement specific functionality, using code uses the Dispatch() // To implement specific functionality, using code uses the Dispatch()
// method to send a method name and body, and get back a response. So // method to send a method name and body, and get back a response. So
@ -273,7 +276,8 @@ type AmqpRPCCLient struct {
log *blog.AuditLogger 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() hostname, err := os.Hostname()
if err != nil { if err != nil {
return nil, err return nil, err
@ -316,10 +320,15 @@ func NewAmqpRPCCLient(clientQueuePrefix, serverQueue string, channel *amqp.Chann
return rpc, err 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) { func (rpc *AmqpRPCCLient) SetTimeout(ttl time.Duration) {
rpc.timeout = ttl 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 { func (rpc *AmqpRPCCLient) Dispatch(method string, body []byte) chan []byte {
// Create a channel on which to direct the response // Create a channel on which to direct the response
// At least in some cases, it's important that this channel // 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 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) { func (rpc *AmqpRPCCLient) DispatchSync(method string, body []byte) (response []byte, err error) {
select { select {
case jsonResponse := <-rpc.Dispatch(method, body): case jsonResponse := <-rpc.Dispatch(method, body):
@ -365,8 +375,3 @@ func (rpc *AmqpRPCCLient) DispatchSync(method string, body []byte) (response []b
return return
} }
} }
func (rpc *AmqpRPCCLient) SyncDispatchWithTimeout(method string, body []byte, ttl time.Duration) (response []byte, err error) {
err = errors.New("Not Implemented")
return
}

View File

@ -14,7 +14,6 @@ type RPCClient interface {
SetTimeout(time.Duration) SetTimeout(time.Duration)
Dispatch(string, []byte) chan []byte Dispatch(string, []byte) chan []byte
DispatchSync(string, []byte) ([]byte, error) DispatchSync(string, []byte) ([]byte, error)
SyncDispatchWithTimeout(string, []byte, time.Duration) ([]byte, error)
} }
// RPCServer describes the functions an RPC Server performs // RPCServer describes the functions an RPC Server performs

View File

@ -32,6 +32,7 @@ import (
// The WebFrontEnd role does not expose any functionality over RPC, // The WebFrontEnd role does not expose any functionality over RPC,
// so it doesn't need wrappers. // so it doesn't need wrappers.
// These strings are used by the RPC layer to identify function points.
const ( const (
MethodNewRegistration = "NewRegistration" // RA, SA MethodNewRegistration = "NewRegistration" // RA, SA
MethodNewAuthorization = "NewAuthorization" // RA 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)) 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 { func NewRegistrationAuthorityServer(rpc RPCServer, impl core.RegistrationAuthority) error {
log := blog.GetAuditLogger() log := blog.GetAuditLogger()
@ -289,15 +291,18 @@ func NewRegistrationAuthorityServer(rpc RPCServer, impl core.RegistrationAuthori
return nil return nil
} }
// RegistrationAuthorityClient represents an RA RPC client
type RegistrationAuthorityClient struct { type RegistrationAuthorityClient struct {
rpc RPCClient rpc RPCClient
} }
// NewRegistrationAuthorityClient constructs an RPC client
func NewRegistrationAuthorityClient(client RPCClient) (rac RegistrationAuthorityClient, err error) { func NewRegistrationAuthorityClient(client RPCClient) (rac RegistrationAuthorityClient, err error) {
rac = RegistrationAuthorityClient{rpc: client} rac = RegistrationAuthorityClient{rpc: client}
return return
} }
// NewRegistration sends a New Registration request
func (rac RegistrationAuthorityClient) NewRegistration(reg core.Registration) (newReg core.Registration, err error) { func (rac RegistrationAuthorityClient) NewRegistration(reg core.Registration) (newReg core.Registration, err error) {
data, err := json.Marshal(registrationRequest{reg}) data, err := json.Marshal(registrationRequest{reg})
if err != nil { if err != nil {
@ -313,6 +318,7 @@ func (rac RegistrationAuthorityClient) NewRegistration(reg core.Registration) (n
return return
} }
// NewAuthorization sends a New Authorization request
func (rac RegistrationAuthorityClient) NewAuthorization(authz core.Authorization, regID int64) (newAuthz core.Authorization, err error) { func (rac RegistrationAuthorityClient) NewAuthorization(authz core.Authorization, regID int64) (newAuthz core.Authorization, err error) {
data, err := json.Marshal(authorizationRequest{authz, regID}) data, err := json.Marshal(authorizationRequest{authz, regID})
if err != nil { if err != nil {
@ -328,6 +334,7 @@ func (rac RegistrationAuthorityClient) NewAuthorization(authz core.Authorization
return return
} }
// NewCertificate sends a New Certificate request
func (rac RegistrationAuthorityClient) NewCertificate(cr core.CertificateRequest, regID int64) (cert core.Certificate, err error) { func (rac RegistrationAuthorityClient) NewCertificate(cr core.CertificateRequest, regID int64) (cert core.Certificate, err error) {
data, err := json.Marshal(certificateRequest{cr, regID}) data, err := json.Marshal(certificateRequest{cr, regID})
if err != nil { if err != nil {
@ -343,6 +350,7 @@ func (rac RegistrationAuthorityClient) NewCertificate(cr core.CertificateRequest
return return
} }
// UpdateRegistration sends an Update Registration request
func (rac RegistrationAuthorityClient) UpdateRegistration(base core.Registration, update core.Registration) (newReg core.Registration, err error) { func (rac RegistrationAuthorityClient) UpdateRegistration(base core.Registration, update core.Registration) (newReg core.Registration, err error) {
var urReq updateRegistrationRequest var urReq updateRegistrationRequest
urReq.Base = base urReq.Base = base
@ -362,6 +370,7 @@ func (rac RegistrationAuthorityClient) UpdateRegistration(base core.Registration
return return
} }
// UpdateAuthorization sends an Update Authorization request
func (rac RegistrationAuthorityClient) UpdateAuthorization(authz core.Authorization, index int, response core.Challenge) (newAuthz core.Authorization, err error) { func (rac RegistrationAuthorityClient) UpdateAuthorization(authz core.Authorization, index int, response core.Challenge) (newAuthz core.Authorization, err error) {
var uaReq updateAuthorizationRequest var uaReq updateAuthorizationRequest
uaReq.Authz = authz uaReq.Authz = authz
@ -382,11 +391,13 @@ func (rac RegistrationAuthorityClient) UpdateAuthorization(authz core.Authorizat
return return
} }
// RevokeCertificate sends a Revoke Certificate request
func (rac RegistrationAuthorityClient) RevokeCertificate(cert x509.Certificate) (err error) { func (rac RegistrationAuthorityClient) RevokeCertificate(cert x509.Certificate) (err error) {
_, err = rac.rpc.DispatchSync(MethodRevokeCertificate, cert.Raw) _, err = rac.rpc.DispatchSync(MethodRevokeCertificate, cert.Raw)
return return
} }
// OnValidationUpdate senda a notice that a validation has updated
func (rac RegistrationAuthorityClient) OnValidationUpdate(authz core.Authorization) (err error) { func (rac RegistrationAuthorityClient) OnValidationUpdate(authz core.Authorization) (err error) {
data, err := json.Marshal(authz) data, err := json.Marshal(authz)
if err != nil { if err != nil {
@ -397,6 +408,8 @@ func (rac RegistrationAuthorityClient) OnValidationUpdate(authz core.Authorizati
return return
} }
// NewValidationAuthorityServer constructs an RPC server
//
// ValidationAuthorityClient / Server // ValidationAuthorityClient / Server
// -> UpdateValidations // -> UpdateValidations
func NewValidationAuthorityServer(rpc RPCServer, impl core.ValidationAuthority) (err error) { func NewValidationAuthorityServer(rpc RPCServer, impl core.ValidationAuthority) (err error) {
@ -441,15 +454,18 @@ func NewValidationAuthorityServer(rpc RPCServer, impl core.ValidationAuthority)
return nil return nil
} }
// ValidationAuthorityClient represents an RPC client for the VA
type ValidationAuthorityClient struct { type ValidationAuthorityClient struct {
rpc RPCClient rpc RPCClient
} }
// NewValidationAuthorityClient constructs an RPC client
func NewValidationAuthorityClient(client RPCClient) (vac ValidationAuthorityClient, err error) { func NewValidationAuthorityClient(client RPCClient) (vac ValidationAuthorityClient, err error) {
vac = ValidationAuthorityClient{rpc: client} vac = ValidationAuthorityClient{rpc: client}
return return
} }
// UpdateValidations sends an Update Validations request
func (vac ValidationAuthorityClient) UpdateValidations(authz core.Authorization, index int) error { func (vac ValidationAuthorityClient) UpdateValidations(authz core.Authorization, index int) error {
var vaReq validationRequest var vaReq validationRequest
vaReq.Authz = authz vaReq.Authz = authz
@ -463,6 +479,7 @@ func (vac ValidationAuthorityClient) UpdateValidations(authz core.Authorization,
return nil return nil
} }
// CheckCAARecords sends a request to check CAA records
func (vac ValidationAuthorityClient) CheckCAARecords(ident core.AcmeIdentifier) (present bool, valid bool, err error) { func (vac ValidationAuthorityClient) CheckCAARecords(ident core.AcmeIdentifier) (present bool, valid bool, err error) {
var caaReq caaRequest var caaReq caaRequest
caaReq.Ident = ident caaReq.Ident = ident
@ -487,6 +504,8 @@ func (vac ValidationAuthorityClient) CheckCAARecords(ident core.AcmeIdentifier)
return return
} }
// NewCertificateAuthorityServer constructs an RPC server
//
// CertificateAuthorityClient / Server // CertificateAuthorityClient / Server
// -> IssueCertificate // -> IssueCertificate
func NewCertificateAuthorityServer(rpc RPCServer, impl core.CertificateAuthority) (err error) { func NewCertificateAuthorityServer(rpc RPCServer, impl core.CertificateAuthority) (err error) {
@ -554,15 +573,18 @@ func NewCertificateAuthorityServer(rpc RPCServer, impl core.CertificateAuthority
return nil return nil
} }
// CertificateAuthorityClient is a client to communicate with the CA.
type CertificateAuthorityClient struct { type CertificateAuthorityClient struct {
rpc RPCClient rpc RPCClient
} }
// NewCertificateAuthorityClient constructs an RPC client
func NewCertificateAuthorityClient(client RPCClient) (cac CertificateAuthorityClient, err error) { func NewCertificateAuthorityClient(client RPCClient) (cac CertificateAuthorityClient, err error) {
cac = CertificateAuthorityClient{rpc: client} cac = CertificateAuthorityClient{rpc: client}
return 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) { func (cac CertificateAuthorityClient) IssueCertificate(csr x509.CertificateRequest, regID int64, earliestExpiry time.Time) (cert core.Certificate, err error) {
var icReq issueCertificateRequest var icReq issueCertificateRequest
icReq.Bytes = csr.Raw icReq.Bytes = csr.Raw
@ -581,6 +603,7 @@ func (cac CertificateAuthorityClient) IssueCertificate(csr x509.CertificateReque
return return
} }
// RevokeCertificate sends a request to revoke a certificate
func (cac CertificateAuthorityClient) RevokeCertificate(serial string, reasonCode int) (err error) { func (cac CertificateAuthorityClient) RevokeCertificate(serial string, reasonCode int) (err error) {
var revokeReq revokeCertificateRequest var revokeReq revokeCertificateRequest
revokeReq.Serial = serial revokeReq.Serial = serial
@ -597,6 +620,7 @@ func (cac CertificateAuthorityClient) RevokeCertificate(serial string, reasonCod
return return
} }
// GenerateOCSP sends a request to generate an OCSP response
func (cac CertificateAuthorityClient) GenerateOCSP(signRequest core.OCSPSigningRequest) (resp []byte, err error) { func (cac CertificateAuthorityClient) GenerateOCSP(signRequest core.OCSPSigningRequest) (resp []byte, err error) {
data, err := json.Marshal(signRequest) data, err := json.Marshal(signRequest)
if err != nil { if err != nil {
@ -616,6 +640,7 @@ func (cac CertificateAuthorityClient) GenerateOCSP(signRequest core.OCSPSigningR
return return
} }
// NewStorageAuthorityServer constructs an RPC server
func NewStorageAuthorityServer(rpc RPCServer, impl core.StorageAuthority) error { func NewStorageAuthorityServer(rpc RPCServer, impl core.StorageAuthority) error {
rpc.Handle(MethodUpdateRegistration, func(req []byte) (response []byte, err error) { rpc.Handle(MethodUpdateRegistration, func(req []byte) (response []byte, err error) {
var reg core.Registration var reg core.Registration
@ -874,15 +899,18 @@ func NewStorageAuthorityServer(rpc RPCServer, impl core.StorageAuthority) error
return nil return nil
} }
// StorageAuthorityClient is a client to communicate with the Storage Authority
type StorageAuthorityClient struct { type StorageAuthorityClient struct {
rpc RPCClient rpc RPCClient
} }
// NewStorageAuthorityClient constructs an RPC client
func NewStorageAuthorityClient(client RPCClient) (sac StorageAuthorityClient, err error) { func NewStorageAuthorityClient(client RPCClient) (sac StorageAuthorityClient, err error) {
sac = StorageAuthorityClient{rpc: client} sac = StorageAuthorityClient{rpc: client}
return return
} }
// GetRegistration sends a request to get a registration by ID
func (cac StorageAuthorityClient) GetRegistration(id int64) (reg core.Registration, err error) { func (cac StorageAuthorityClient) GetRegistration(id int64) (reg core.Registration, err error) {
var grReq getRegistrationRequest var grReq getRegistrationRequest
grReq.ID = id grReq.ID = id
@ -901,6 +929,7 @@ func (cac StorageAuthorityClient) GetRegistration(id int64) (reg core.Registrati
return return
} }
// GetRegistrationByKey sends a request to get a registration by JWK
func (cac StorageAuthorityClient) GetRegistrationByKey(key jose.JsonWebKey) (reg core.Registration, err error) { func (cac StorageAuthorityClient) GetRegistrationByKey(key jose.JsonWebKey) (reg core.Registration, err error) {
jsonKey, err := key.MarshalJSON() jsonKey, err := key.MarshalJSON()
if err != nil { if err != nil {
@ -916,6 +945,7 @@ func (cac StorageAuthorityClient) GetRegistrationByKey(key jose.JsonWebKey) (reg
return return
} }
// GetAuthorization sends a request to get an Authorization by ID
func (cac StorageAuthorityClient) GetAuthorization(id string) (authz core.Authorization, err error) { func (cac StorageAuthorityClient) GetAuthorization(id string) (authz core.Authorization, err error) {
jsonAuthz, err := cac.rpc.DispatchSync(MethodGetAuthorization, []byte(id)) jsonAuthz, err := cac.rpc.DispatchSync(MethodGetAuthorization, []byte(id))
if err != nil { if err != nil {
@ -926,6 +956,7 @@ func (cac StorageAuthorityClient) GetAuthorization(id string) (authz core.Author
return return
} }
// GetCertificate sends a request to get a Certificate by ID
func (cac StorageAuthorityClient) GetCertificate(id string) (cert core.Certificate, err error) { func (cac StorageAuthorityClient) GetCertificate(id string) (cert core.Certificate, err error) {
jsonCert, err := cac.rpc.DispatchSync(MethodGetCertificate, []byte(id)) jsonCert, err := cac.rpc.DispatchSync(MethodGetCertificate, []byte(id))
if err != nil { if err != nil {
@ -936,6 +967,8 @@ func (cac StorageAuthorityClient) GetCertificate(id string) (cert core.Certifica
return 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) { func (cac StorageAuthorityClient) GetCertificateByShortSerial(id string) (cert core.Certificate, err error) {
jsonCert, err := cac.rpc.DispatchSync(MethodGetCertificateByShortSerial, []byte(id)) jsonCert, err := cac.rpc.DispatchSync(MethodGetCertificateByShortSerial, []byte(id))
if err != nil { if err != nil {
@ -946,6 +979,8 @@ func (cac StorageAuthorityClient) GetCertificateByShortSerial(id string) (cert c
return 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) { func (cac StorageAuthorityClient) GetCertificateStatus(id string) (status core.CertificateStatus, err error) {
jsonStatus, err := cac.rpc.DispatchSync(MethodGetCertificateStatus, []byte(id)) jsonStatus, err := cac.rpc.DispatchSync(MethodGetCertificateStatus, []byte(id))
if err != nil { if err != nil {
@ -956,6 +991,7 @@ func (cac StorageAuthorityClient) GetCertificateStatus(id string) (status core.C
return return
} }
// MarkCertificateRevoked sends a request to mark a certificate as revoked
func (cac StorageAuthorityClient) MarkCertificateRevoked(serial string, ocspResponse []byte, reasonCode int) (err error) { func (cac StorageAuthorityClient) MarkCertificateRevoked(serial string, ocspResponse []byte, reasonCode int) (err error) {
var mcrReq markCertificateRevokedRequest var mcrReq markCertificateRevokedRequest
@ -972,6 +1008,7 @@ func (cac StorageAuthorityClient) MarkCertificateRevoked(serial string, ocspResp
return return
} }
// UpdateOCSP sends a request to store an updated OCSP response
func (cac StorageAuthorityClient) UpdateOCSP(serial string, ocspResponse []byte) (err error) { func (cac StorageAuthorityClient) UpdateOCSP(serial string, ocspResponse []byte) (err error) {
var updateOCSPReq updateOCSPRequest var updateOCSPReq updateOCSPRequest
@ -987,6 +1024,7 @@ func (cac StorageAuthorityClient) UpdateOCSP(serial string, ocspResponse []byte)
return return
} }
// UpdateRegistration sends a request to store an updated registration
func (cac StorageAuthorityClient) UpdateRegistration(reg core.Registration) (err error) { func (cac StorageAuthorityClient) UpdateRegistration(reg core.Registration) (err error) {
jsonReg, err := json.Marshal(reg) jsonReg, err := json.Marshal(reg)
if err != nil { if err != nil {
@ -997,6 +1035,7 @@ func (cac StorageAuthorityClient) UpdateRegistration(reg core.Registration) (err
return return
} }
// NewRegistration sends a request to store a new registration
func (cac StorageAuthorityClient) NewRegistration(reg core.Registration) (output core.Registration, err error) { func (cac StorageAuthorityClient) NewRegistration(reg core.Registration) (output core.Registration, err error) {
jsonReg, err := json.Marshal(reg) jsonReg, err := json.Marshal(reg)
if err != nil { if err != nil {
@ -1015,6 +1054,7 @@ func (cac StorageAuthorityClient) NewRegistration(reg core.Registration) (output
return output, nil return output, nil
} }
// NewPendingAuthorization sends a request to store a pending authorization
func (cac StorageAuthorityClient) NewPendingAuthorization(authz core.Authorization) (output core.Authorization, err error) { func (cac StorageAuthorityClient) NewPendingAuthorization(authz core.Authorization) (output core.Authorization, err error) {
jsonAuthz, err := json.Marshal(authz) jsonAuthz, err := json.Marshal(authz)
if err != nil { if err != nil {
@ -1032,6 +1072,8 @@ func (cac StorageAuthorityClient) NewPendingAuthorization(authz core.Authorizati
return return
} }
// UpdatePendingAuthorization sends a request to update the data in a pending
// authorization
func (cac StorageAuthorityClient) UpdatePendingAuthorization(authz core.Authorization) (err error) { func (cac StorageAuthorityClient) UpdatePendingAuthorization(authz core.Authorization) (err error) {
jsonAuthz, err := json.Marshal(authz) jsonAuthz, err := json.Marshal(authz)
if err != nil { if err != nil {
@ -1042,6 +1084,8 @@ func (cac StorageAuthorityClient) UpdatePendingAuthorization(authz core.Authoriz
return return
} }
// FinalizeAuthorization sends a request to finalize an authorization (convert
// from pending)
func (cac StorageAuthorityClient) FinalizeAuthorization(authz core.Authorization) (err error) { func (cac StorageAuthorityClient) FinalizeAuthorization(authz core.Authorization) (err error) {
jsonAuthz, err := json.Marshal(authz) jsonAuthz, err := json.Marshal(authz)
if err != nil { if err != nil {
@ -1052,6 +1096,7 @@ func (cac StorageAuthorityClient) FinalizeAuthorization(authz core.Authorization
return return
} }
// AddCertificate sends a request to record the issuance of a certificate
func (cac StorageAuthorityClient) AddCertificate(cert []byte, regID int64) (id string, err error) { func (cac StorageAuthorityClient) AddCertificate(cert []byte, regID int64) (id string, err error) {
var acReq addCertificateRequest var acReq addCertificateRequest
acReq.Bytes = cert acReq.Bytes = cert
@ -1069,6 +1114,7 @@ func (cac StorageAuthorityClient) AddCertificate(cert []byte, regID int64) (id s
return return
} }
// AlreadyDeniedCSR sends a request to search for denied names
func (cac StorageAuthorityClient) AlreadyDeniedCSR(names []string) (exists bool, err error) { func (cac StorageAuthorityClient) AlreadyDeniedCSR(names []string) (exists bool, err error) {
var adcReq alreadyDeniedCSRReq var adcReq alreadyDeniedCSRReq
adcReq.Names = names adcReq.Names = names

View File

@ -16,7 +16,7 @@ import (
"github.com/letsencrypt/boulder/test" "github.com/letsencrypt/boulder/test"
) )
const JWK_1_JSON = `{ const JWK1JSON = `{
"kty": "RSA", "kty": "RSA",
"n": "vuc785P8lBj3fUxyZchF_uZw6WtbxcorqgTyq-qapF5lrO1U82Tp93rpXlmctj6fyFHBVVB5aXnUHJ7LZeVPod7Wnfl8p5OyhlHQHC8BnzdzCqCMKmWZNX5DtETDId0qzU7dPzh0LP0idt5buU7L9QNaabChw3nnaL47iu_1Di5Wp264p2TwACeedv2hfRDjDlJmaQXuS8Rtv9GnRWyC9JBu7XmGvGDziumnJH7Hyzh3VNu-kSPQD3vuAFgMZS6uUzOztCkT0fpOalZI6hqxtWLvXUMj-crXrn-Maavz8qRhpAyp5kcYk3jiHGgQIi7QSK2JIdRJ8APyX9HlmTN5AQ", "n": "vuc785P8lBj3fUxyZchF_uZw6WtbxcorqgTyq-qapF5lrO1U82Tp93rpXlmctj6fyFHBVVB5aXnUHJ7LZeVPod7Wnfl8p5OyhlHQHC8BnzdzCqCMKmWZNX5DtETDId0qzU7dPzh0LP0idt5buU7L9QNaabChw3nnaL47iu_1Di5Wp264p2TwACeedv2hfRDjDlJmaQXuS8Rtv9GnRWyC9JBu7XmGvGDziumnJH7Hyzh3VNu-kSPQD3vuAFgMZS6uUzOztCkT0fpOalZI6hqxtWLvXUMj-crXrn-Maavz8qRhpAyp5kcYk3jiHGgQIi7QSK2JIdRJ8APyX9HlmTN5AQ",
"e": "AAEAAQ" "e": "AAEAAQ"
@ -59,12 +59,6 @@ func (rpc *MockRPCClient) DispatchSync(method string, body []byte) (response []b
return 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) { func TestRANewRegistration(t *testing.T) {
mock := &MockRPCClient{} mock := &MockRPCClient{}
client, err := NewRegistrationAuthorityClient(mock) client, err := NewRegistrationAuthorityClient(mock)
@ -72,7 +66,7 @@ func TestRANewRegistration(t *testing.T) {
test.AssertNotNil(t, client, "Client construction") test.AssertNotNil(t, client, "Client construction")
var jwk jose.JsonWebKey var jwk jose.JsonWebKey
json.Unmarshal([]byte(JWK_1_JSON), &jwk) json.Unmarshal([]byte(JWK1JSON), &jwk)
reg := core.Registration{ reg := core.Registration{
ID: 1, ID: 1,

View File

@ -19,7 +19,7 @@ import (
blog "github.com/letsencrypt/boulder/log" blog "github.com/letsencrypt/boulder/log"
) )
var dialectMap map[string]interface{} = map[string]interface{}{ var dialectMap = map[string]interface{}{
"sqlite3": gorp.SqliteDialect{}, "sqlite3": gorp.SqliteDialect{},
"mysql": gorp.MySQLDialect{Engine: "InnoDB", Encoding: "UTF8"}, "mysql": gorp.MySQLDialect{Engine: "InnoDB", Encoding: "UTF8"},
"postgres": gorp.PostgresDialect{}, "postgres": gorp.PostgresDialect{},

View File

@ -22,6 +22,7 @@ import (
blog "github.com/letsencrypt/boulder/log" blog "github.com/letsencrypt/boulder/log"
) )
// SQLStorageAuthority defines a Storage Authority
type SQLStorageAuthority struct { type SQLStorageAuthority struct {
dbMap *gorp.DbMap dbMap *gorp.DbMap
bucket map[string]interface{} // XXX included only for backward compat 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 return count > 0
} }
// GetRegistration obtains a Registration by ID
func (ssa *SQLStorageAuthority) GetRegistration(id int64) (reg core.Registration, err error) { func (ssa *SQLStorageAuthority) GetRegistration(id int64) (reg core.Registration, err error) {
regObj, err := ssa.dbMap.Get(core.Registration{}, id) regObj, err := ssa.dbMap.Get(core.Registration{}, id)
if err != nil { if err != nil {
@ -117,16 +119,18 @@ func (ssa *SQLStorageAuthority) GetRegistration(id int64) (reg core.Registration
return return
} }
// GetRegistrationByKey obtains a Registration by JWK
func (ssa *SQLStorageAuthority) GetRegistrationByKey(key jose.JsonWebKey) (reg core.Registration, err error) { 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 { if err != nil {
return return
} }
err = ssa.dbMap.SelectOne(&reg, "SELECT * FROM registrations WHERE jwk = :key", map[string]interface{}{"key": string(keyJson)}) err = ssa.dbMap.SelectOne(&reg, "SELECT * FROM registrations WHERE jwk = :key", map[string]interface{}{"key": string(keyJSON)})
return return
} }
// GetAuthorization obtains an Authorization by ID
func (ssa *SQLStorageAuthority) GetAuthorization(id string) (authz core.Authorization, err error) { func (ssa *SQLStorageAuthority) GetAuthorization(id string) (authz core.Authorization, err error) {
tx, err := ssa.dbMap.Begin() tx, err := ssa.dbMap.Begin()
if err != nil { if err != nil {
@ -220,6 +224,7 @@ func (ssa *SQLStorageAuthority) GetCertificateStatus(serial string) (status core
return return
} }
// NewRegistration stores a new Registration
func (ssa *SQLStorageAuthority) NewRegistration(reg core.Registration) (core.Registration, error) { func (ssa *SQLStorageAuthority) NewRegistration(reg core.Registration) (core.Registration, error) {
tx, err := ssa.dbMap.Begin() tx, err := ssa.dbMap.Begin()
if err != nil { 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) { func (ssa *SQLStorageAuthority) UpdateOCSP(serial string, ocspResponse []byte) (err error) {
status, err := ssa.GetCertificateStatus(serial) status, err := ssa.GetCertificateStatus(serial)
if err != nil { if err != nil {
return errors.New(fmt.Sprintf( return fmt.Errorf(
"Unable to update OCSP for certificate %s: cert status not found.", serial)) "Unable to update OCSP for certificate %s: cert status not found.", serial)
} }
tx, err := ssa.dbMap.Begin() tx, err := ssa.dbMap.Begin()
@ -275,13 +280,13 @@ func (ssa *SQLStorageAuthority) UpdateOCSP(serial string, ocspResponse []byte) (
// with a timestamp and a reason. // with a timestamp and a reason.
func (ssa *SQLStorageAuthority) MarkCertificateRevoked(serial string, ocspResponse []byte, reasonCode int) (err error) { func (ssa *SQLStorageAuthority) MarkCertificateRevoked(serial string, ocspResponse []byte, reasonCode int) (err error) {
if _, err = ssa.GetCertificate(serial); err != nil { if _, err = ssa.GetCertificate(serial); err != nil {
return errors.New(fmt.Sprintf( return fmt.Errorf(
"Unable to mark certificate %s revoked: cert not found.", serial)) "Unable to mark certificate %s revoked: cert not found.", serial)
} }
if _, err = ssa.GetCertificateStatus(serial); err != nil { if _, err = ssa.GetCertificateStatus(serial); err != nil {
return errors.New(fmt.Sprintf( return fmt.Errorf(
"Unable to mark certificate %s revoked: cert status not found.", serial)) "Unable to mark certificate %s revoked: cert status not found.", serial)
} }
tx, err := ssa.dbMap.Begin() tx, err := ssa.dbMap.Begin()
@ -321,6 +326,7 @@ func (ssa *SQLStorageAuthority) MarkCertificateRevoked(serial string, ocspRespon
return return
} }
// UpdateRegistration stores an updated Registration
func (ssa *SQLStorageAuthority) UpdateRegistration(reg core.Registration) (err error) { func (ssa *SQLStorageAuthority) UpdateRegistration(reg core.Registration) (err error) {
tx, err := ssa.dbMap.Begin() tx, err := ssa.dbMap.Begin()
if err != nil { if err != nil {
@ -343,6 +349,7 @@ func (ssa *SQLStorageAuthority) UpdateRegistration(reg core.Registration) (err e
return return
} }
// NewPendingAuthorization stores a new Pending Authorization
func (ssa *SQLStorageAuthority) NewPendingAuthorization(authz core.Authorization) (output core.Authorization, err error) { func (ssa *SQLStorageAuthority) NewPendingAuthorization(authz core.Authorization) (output core.Authorization, err error) {
tx, err := ssa.dbMap.Begin() tx, err := ssa.dbMap.Begin()
if err != nil { if err != nil {
@ -356,18 +363,19 @@ func (ssa *SQLStorageAuthority) NewPendingAuthorization(authz core.Authorization
} }
// Insert a stub row in pending // Insert a stub row in pending
pending_authz := pendingauthzModel{Authorization: authz} pendingAuthz := pendingauthzModel{Authorization: authz}
err = tx.Insert(&pending_authz) err = tx.Insert(&pendingAuthz)
if err != nil { if err != nil {
tx.Rollback() tx.Rollback()
return return
} }
err = tx.Commit() err = tx.Commit()
output = pending_authz.Authorization output = pendingAuthz.Authorization
return return
} }
// UpdatePendingAuthorization updates a Pending Authorization
func (ssa *SQLStorageAuthority) UpdatePendingAuthorization(authz core.Authorization) (err error) { func (ssa *SQLStorageAuthority) UpdatePendingAuthorization(authz core.Authorization) (err error) {
tx, err := ssa.dbMap.Begin() tx, err := ssa.dbMap.Begin()
if err != nil { if err != nil {
@ -409,6 +417,7 @@ func (ssa *SQLStorageAuthority) UpdatePendingAuthorization(authz core.Authorizat
return return
} }
// FinalizeAuthorization converts a Pending Authorization to a final one
func (ssa *SQLStorageAuthority) FinalizeAuthorization(authz core.Authorization) (err error) { func (ssa *SQLStorageAuthority) FinalizeAuthorization(authz core.Authorization) (err error) {
tx, err := ssa.dbMap.Begin() tx, err := ssa.dbMap.Begin()
if err != nil { if err != nil {
@ -463,6 +472,7 @@ func (ssa *SQLStorageAuthority) FinalizeAuthorization(authz core.Authorization)
return return
} }
// AddCertificate stores an issued certificate.
func (ssa *SQLStorageAuthority) AddCertificate(certDER []byte, regID int64) (digest string, err error) { func (ssa *SQLStorageAuthority) AddCertificate(certDER []byte, regID int64) (digest string, err error) {
var parsedCertificate *x509.Certificate var parsedCertificate *x509.Certificate
parsedCertificate, err = x509.ParseCertificate(certDER) parsedCertificate, err = x509.ParseCertificate(certDER)
@ -512,6 +522,7 @@ func (ssa *SQLStorageAuthority) AddCertificate(certDER []byte, regID int64) (dig
return return
} }
// AlreadyDeniedCSR queries to find if the name list has already been denied.
func (ssa *SQLStorageAuthority) AlreadyDeniedCSR(names []string) (already bool, err error) { func (ssa *SQLStorageAuthority) AlreadyDeniedCSR(names []string) (already bool, err error) {
sort.Strings(names) sort.Strings(names)

View File

@ -34,7 +34,7 @@ func initSA(t *testing.T) *SQLStorageAuthority {
return sa return sa
} }
var theKey string = `{ var theKey = `{
"kty": "RSA", "kty": "RSA",
"n": "n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqVwGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuCLqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5gHdrNP5zw", "n": "n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqVwGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuCLqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5gHdrNP5zw",
"e": "AQAB" "e": "AQAB"

View File

@ -38,7 +38,7 @@ func (tc BoulderTypeConverter) ToDb(val interface{}) (interface{}, error) {
return string(t), nil return string(t), nil
case core.OCSPStatus: case core.OCSPStatus:
return string(t), nil return string(t), nil
case core.JsonBuffer: case core.JSONBuffer:
return []byte(t), nil return []byte(t), nil
default: default:
return val, nil 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. // FromDb converts a DB representation back into a Boulder object.
func (tc BoulderTypeConverter) FromDb(target interface{}) (gorp.CustomScanner, bool) { func (tc BoulderTypeConverter) FromDb(target interface{}) (gorp.CustomScanner, bool) {
switch target.(type) { 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 { binder := func(holder, target interface{}) error {
s, ok := holder.(*string) s, ok := holder.(*string)
if !ok { if !ok {

View File

@ -15,7 +15,7 @@ import (
jose "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/square/go-jose" jose "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/square/go-jose"
) )
const JWK_1_JSON = `{ const JWK1JSON = `{
"kty": "RSA", "kty": "RSA",
"n": "vuc785P8lBj3fUxyZchF_uZw6WtbxcorqgTyq-qapF5lrO1U82Tp93rpXlmctj6fyFHBVVB5aXnUHJ7LZeVPod7Wnfl8p5OyhlHQHC8BnzdzCqCMKmWZNX5DtETDId0qzU7dPzh0LP0idt5buU7L9QNaabChw3nnaL47iu_1Di5Wp264p2TwACeedv2hfRDjDlJmaQXuS8Rtv9GnRWyC9JBu7XmGvGDziumnJH7Hyzh3VNu-kSPQD3vuAFgMZS6uUzOztCkT0fpOalZI6hqxtWLvXUMj-crXrn-Maavz8qRhpAyp5kcYk3jiHGgQIi7QSK2JIdRJ8APyX9HlmTN5AQ", "n": "vuc785P8lBj3fUxyZchF_uZw6WtbxcorqgTyq-qapF5lrO1U82Tp93rpXlmctj6fyFHBVVB5aXnUHJ7LZeVPod7Wnfl8p5OyhlHQHC8BnzdzCqCMKmWZNX5DtETDId0qzU7dPzh0LP0idt5buU7L9QNaabChw3nnaL47iu_1Di5Wp264p2TwACeedv2hfRDjDlJmaQXuS8Rtv9GnRWyC9JBu7XmGvGDziumnJH7Hyzh3VNu-kSPQD3vuAFgMZS6uUzOztCkT0fpOalZI6hqxtWLvXUMj-crXrn-Maavz8qRhpAyp5kcYk3jiHGgQIi7QSK2JIdRJ8APyX9HlmTN5AQ",
"e": "AAEAAQ" "e": "AAEAAQ"
@ -46,7 +46,7 @@ func TestJsonWebKey(t *testing.T) {
tc := BoulderTypeConverter{} tc := BoulderTypeConverter{}
var jwk, out jose.JsonWebKey var jwk, out jose.JsonWebKey
json.Unmarshal([]byte(JWK_1_JSON), &jwk) json.Unmarshal([]byte(JWK1JSON), &jwk)
marshaledI, err := tc.ToDb(jwk) marshaledI, err := tc.ToDb(jwk)
test.AssertNotError(t, err, "Could not ToDb") test.AssertNotError(t, err, "Could not ToDb")

View File

@ -8,15 +8,18 @@ package test
import ( import (
"database/sql" "database/sql"
// Load SQLite3 for test purposes
_ "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/mattn/go-sqlite3" _ "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/mattn/go-sqlite3"
gorp "github.com/letsencrypt/boulder/Godeps/_workspace/src/gopkg.in/gorp.v1" gorp "github.com/letsencrypt/boulder/Godeps/_workspace/src/gopkg.in/gorp.v1"
) )
// MockCADatabase is a mock
type MockCADatabase struct { type MockCADatabase struct {
db *gorp.DbMap db *gorp.DbMap
count int64 count int64
} }
// NewMockCertificateAuthorityDatabase is a mock
func NewMockCertificateAuthorityDatabase() (mock *MockCADatabase, err error) { func NewMockCertificateAuthorityDatabase() (mock *MockCADatabase, err error) {
db, err := sql.Open("sqlite3", ":memory:") db, err := sql.Open("sqlite3", ":memory:")
dbmap := &gorp.DbMap{Db: db, Dialect: gorp.SqliteDialect{}} dbmap := &gorp.DbMap{Db: db, Dialect: gorp.SqliteDialect{}}
@ -24,15 +27,18 @@ func NewMockCertificateAuthorityDatabase() (mock *MockCADatabase, err error) {
return mock, err return mock, err
} }
// Begin is a mock
func (cadb *MockCADatabase) Begin() (*gorp.Transaction, error) { func (cadb *MockCADatabase) Begin() (*gorp.Transaction, error) {
return cadb.db.Begin() return cadb.db.Begin()
} }
// IncrementAndGetSerial is a mock
func (cadb *MockCADatabase) IncrementAndGetSerial(*gorp.Transaction) (int64, error) { func (cadb *MockCADatabase) IncrementAndGetSerial(*gorp.Transaction) (int64, error) {
cadb.count = cadb.count + 1 cadb.count = cadb.count + 1
return cadb.count, nil return cadb.count, nil
} }
// CreateTablesIfNotExists is a mock
func (cadb *MockCADatabase) CreateTablesIfNotExists() error { func (cadb *MockCADatabase) CreateTablesIfNotExists() error {
return nil return nil
} }

View File

@ -26,42 +26,50 @@ func caller() string {
return fmt.Sprintf("%s:%d:", filename, line) return fmt.Sprintf("%s:%d:", filename, line)
} }
// Assert a boolean
func Assert(t *testing.T, result bool, message string) { func Assert(t *testing.T, result bool, message string) {
if !result { if !result {
t.Error(caller(), message) t.Error(caller(), message)
} }
} }
// AssertNotNil checks an object to be non-nil
func AssertNotNil(t *testing.T, obj interface{}, message string) { func AssertNotNil(t *testing.T, obj interface{}, message string) {
if obj == nil { if obj == nil {
t.Error(caller(), message) t.Error(caller(), message)
} }
} }
// AssertNotError checks that err is nil
func AssertNotError(t *testing.T, err error, message string) { func AssertNotError(t *testing.T, err error, message string) {
if err != nil { if err != nil {
t.Error(caller(), message, ":", err) t.Error(caller(), message, ":", err)
} }
} }
// AssertError checks that err is non-nil
func AssertError(t *testing.T, err error, message string) { func AssertError(t *testing.T, err error, message string) {
if err == nil { if err == nil {
t.Error(caller(), message, ":", err) t.Error(caller(), message, ":", err)
} }
} }
// AssertEquals uses the equality operator (=) to measure one and two
func AssertEquals(t *testing.T, one interface{}, two interface{}) { func AssertEquals(t *testing.T, one interface{}, two interface{}) {
if one != two { if one != two {
t.Errorf("%s [%v] != [%v]", caller(), 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{}) { func AssertDeepEquals(t *testing.T, one interface{}, two interface{}) {
if !reflect.DeepEqual(one, two) { if !reflect.DeepEqual(one, two) {
t.Errorf("%s [%+v] !(deep)= [%+v]", caller(), 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{}) { func AssertMarshaledEquals(t *testing.T, one interface{}, two interface{}) {
oneJSON, err := json.Marshal(one) oneJSON, err := json.Marshal(one)
AssertNotError(t, err, "Could not marshal 1st argument") 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{}) { func AssertNotEquals(t *testing.T, one interface{}, two interface{}) {
if one == two { if one == two {
t.Errorf("%s [%v] == [%v]", caller(), 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) { func AssertByteEquals(t *testing.T, one []byte, two []byte) {
if !bytes.Equal(one, two) { if !bytes.Equal(one, two) {
t.Errorf("%s Byte [%s] != [%s]", 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) { func AssertIntEquals(t *testing.T, one int, two int) {
if one != two { if one != two {
t.Errorf("%s Int [%d] != [%d]", caller(), 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) { func AssertBigIntEquals(t *testing.T, one *big.Int, two *big.Int) {
if one.Cmp(two) != 0 { if one.Cmp(two) != 0 {
t.Errorf("%s Int [%d] != [%d]", caller(), one, two) 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) { func AssertContains(t *testing.T, haystack string, needle string) {
if !strings.Contains(haystack, needle) { if !strings.Contains(haystack, needle) {
t.Errorf("%s String [%s] does not contain [%s]", caller(), 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) { func AssertNotContains(t *testing.T, haystack string, needle string) {
if strings.Contains(haystack, needle) { if strings.Contains(haystack, needle) {
t.Errorf("%s String [%s] contains [%s]", caller(), 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) { func AssertSeverity(t *testing.T, data string, severity int) {
expected := fmt.Sprintf("\"severity\":%d", severity) expected := fmt.Sprintf("\"severity\":%d", severity)
AssertContains(t, data, expected) AssertContains(t, data, expected)

View File

@ -58,7 +58,7 @@ func newCAA(encodedRDATA []byte) *CAA {
return &CAA{flag: flag, tag: tag, valueBuf: valueBuf, value: value} return &CAA{flag: flag, tag: tag, valueBuf: valueBuf, value: value}
} }
// Filtered CAA records // CAASet consists of filtered CAA records
type CAASet struct { type CAASet struct {
issue []*CAA issue []*CAA
issuewild []*CAA issuewild []*CAA

View File

@ -20,6 +20,7 @@ import (
blog "github.com/letsencrypt/boulder/log" blog "github.com/letsencrypt/boulder/log"
) )
// ValidationAuthorityImpl represents a VA
type ValidationAuthorityImpl struct { type ValidationAuthorityImpl struct {
RA core.RegistrationAuthority RA core.RegistrationAuthority
log *blog.AuditLogger log *blog.AuditLogger
@ -28,6 +29,8 @@ type ValidationAuthorityImpl struct {
TestMode bool TestMode bool
} }
// NewValidationAuthorityImpl constructs a new VA, and may place it
// into Test Mode (tm)
func NewValidationAuthorityImpl(tm bool) ValidationAuthorityImpl { func NewValidationAuthorityImpl(tm bool) ValidationAuthorityImpl {
logger := blog.GetAuditLogger() logger := blog.GetAuditLogger()
logger.Notice("Validation Authority Starting") logger.Notice("Validation Authority Starting")
@ -131,8 +134,8 @@ func (va ValidationAuthorityImpl) validateDvsni(identifier core.AcmeIdentifier,
return challenge, err return challenge, err
} }
const DVSNI_SUFFIX = ".acme.invalid" const DVSNIsuffix = ".acme.invalid"
nonceName := challenge.Nonce + DVSNI_SUFFIX nonceName := challenge.Nonce + DVSNIsuffix
R, err := core.B64dec(challenge.R) R, err := core.B64dec(challenge.R)
if err != nil { if err != nil {
@ -266,12 +269,13 @@ func (va ValidationAuthorityImpl) validate(authz core.Authorization, challengeIn
va.RA.OnValidationUpdate(authz) va.RA.OnValidationUpdate(authz)
} }
// UpdateValidations runs the validate() method asynchronously using goroutines.
func (va ValidationAuthorityImpl) UpdateValidations(authz core.Authorization, challengeIndex int) error { func (va ValidationAuthorityImpl) UpdateValidations(authz core.Authorization, challengeIndex int) error {
go va.validate(authz, challengeIndex) go va.validate(authz, challengeIndex)
return nil 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 // records, they authorize the configured CA domain to issue a certificate
func (va *ValidationAuthorityImpl) CheckCAARecords(identifier core.AcmeIdentifier) (present, valid bool, err error) { func (va *ValidationAuthorityImpl) CheckCAARecords(identifier core.AcmeIdentifier) (present, valid bool, err error) {
domain := strings.ToLower(identifier.Value) domain := strings.ToLower(identifier.Value)

View File

@ -36,19 +36,19 @@ func intFromB64(b64 string) int {
return int(bigIntFromB64(b64).Int64()) 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 n = bigIntFromB64("n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqVwGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuCLqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5gHdrNP5zw==")
var e int = intFromB64("AQAB") var e = intFromB64("AQAB")
var d *big.Int = bigIntFromB64("bWUC9B-EFRIo8kpGfh0ZuyGPvMNKvYWNtB_ikiH9k20eT-O1q_I78eiZkpXxXQ0UTEs2LsNRS-8uJbvQ-A1irkwMSMkK1J3XTGgdrhCku9gRldY7sNA_AKZGh-Q661_42rINLRCe8W-nZ34ui_qOfkLnK9QWDDqpaIsA-bMwWWSDFu2MUBYwkHTMEzLYGqOe04noqeq1hExBTHBOBdkMXiuFhUq1BU6l-DqEiWxqg82sXt2h-LMnT3046AOYJoRioz75tSUQfGCshWTBnP5uDjd18kKhyv07lhfSJdrPdM5Plyl21hsFf4L_mHCuoFau7gdsPfHPxxjVOcOpBrQzwQ==") var d = 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 p = bigIntFromB64("uKE2dh-cTf6ERF4k4e_jy78GfPYUIaUyoSSJuBzp3Cubk3OCqs6grT8bR_cu0Dm1MZwWmtdqDyI95HrUeq3MP15vMMON8lHTeZu2lmKvwqW7anV5UzhM1iZ7z4yMkuUwFWoBvyY898EXvRD-hdqRxHlSqAZ192zB3pVFJ0s7pFc=")
var q *big.Int = 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}, PublicKey: rsa.PublicKey{N: n, E: e},
D: d, D: d,
Primes: []*big.Int{p, q}, 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 expectedToken = "THETOKEN"
const pathWrongToken = "wrongtoken" const pathWrongToken = "wrongtoken"
@ -545,7 +545,7 @@ func TestDNSValidationLive(t *testing.T) {
Value: "good.bin.coffee", Value: "good.bin.coffee",
} }
var badIdent core.AcmeIdentifier = core.AcmeIdentifier{ var badIdent = core.AcmeIdentifier{
Type: core.IdentifierType("dns"), Type: core.IdentifierType("dns"),
Value: "bad.bin.coffee", Value: "bad.bin.coffee",
} }

View File

@ -27,6 +27,7 @@ import (
blog "github.com/letsencrypt/boulder/log" blog "github.com/letsencrypt/boulder/log"
) )
// Paths are the ACME-spec identified URL path-segments for various methods
const ( const (
NewRegPath = "/acme/new-reg" NewRegPath = "/acme/new-reg"
RegPath = "/acme/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() { func (wfe *WebFrontEndImpl) HandlePaths() {
wfe.NewReg = wfe.BaseURL + NewRegPath wfe.NewReg = wfe.BaseURL + NewRegPath
wfe.RegBase = wfe.BaseURL + RegPath wfe.RegBase = wfe.BaseURL + RegPath
@ -122,6 +125,7 @@ func (wfe *WebFrontEndImpl) HandlePaths() {
// Method implementations // 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) { func (wfe *WebFrontEndImpl) Index(response http.ResponseWriter, request *http.Request) {
wfe.sendStandardHeaders(response) wfe.sendStandardHeaders(response)
@ -167,6 +171,7 @@ type problem struct {
Detail string `json:"detail,omitempty"` Detail string `json:"detail,omitempty"`
} }
// These are defined problems
const ( const (
MalformedProblem = ProblemType("urn:acme:error:malformed") MalformedProblem = ProblemType("urn:acme:error:malformed")
UnauthorizedProblem = ProblemType("urn:acme:error:unauthorized") UnauthorizedProblem = ProblemType("urn:acme:error:unauthorized")
@ -240,12 +245,11 @@ func (wfe *WebFrontEndImpl) verifyPOST(request *http.Request, regCheck bool) ([]
// registration is an overall failure to verify. // registration is an overall failure to verify.
if regCheck { if regCheck {
return nil, nil, reg, err return nil, nil, reg, err
} else { }
// Otherwise we just return an empty registration. The caller is expected // Otherwise we just return an empty registration. The caller is expected
// to use the returned key instead. // to use the returned key instead.
reg = core.Registration{} reg = core.Registration{}
} }
}
return []byte(payload), key, reg, nil return []byte(payload), key, reg, nil
} }
@ -293,6 +297,7 @@ func link(url, relation string) string {
return fmt.Sprintf("<%s>;rel=\"%s\"", url, relation) 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) { func (wfe *WebFrontEndImpl) NewRegistration(response http.ResponseWriter, request *http.Request) {
wfe.sendStandardHeaders(response) wfe.sendStandardHeaders(response)
@ -356,6 +361,7 @@ func (wfe *WebFrontEndImpl) NewRegistration(response http.ResponseWriter, reques
wfe.Stats.Inc("Registrations", 1, 1.0) 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) { func (wfe *WebFrontEndImpl) NewAuthorization(response http.ResponseWriter, request *http.Request) {
wfe.sendStandardHeaders(response) wfe.sendStandardHeaders(response)
@ -417,6 +423,7 @@ func (wfe *WebFrontEndImpl) NewAuthorization(response http.ResponseWriter, reque
wfe.Stats.Inc("PendingAuthorizations", 1, 1.0) 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) { func (wfe *WebFrontEndImpl) RevokeCertificate(response http.ResponseWriter, request *http.Request) {
wfe.sendStandardHeaders(response) wfe.sendStandardHeaders(response)
@ -435,7 +442,7 @@ func (wfe *WebFrontEndImpl) RevokeCertificate(response http.ResponseWriter, requ
} }
type RevokeRequest struct { type RevokeRequest struct {
CertificateDER core.JsonBuffer `json:"certificate"` CertificateDER core.JSONBuffer `json:"certificate"`
} }
var revokeRequest RevokeRequest var revokeRequest RevokeRequest
if err = json.Unmarshal(body, &revokeRequest); err != nil { 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) { func (wfe *WebFrontEndImpl) NewCertificate(response http.ResponseWriter, request *http.Request) {
wfe.sendStandardHeaders(response) 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) { func (wfe *WebFrontEndImpl) Registration(response http.ResponseWriter, request *http.Request) {
wfe.sendStandardHeaders(response) wfe.sendStandardHeaders(response)
@ -759,6 +769,8 @@ func (wfe *WebFrontEndImpl) Registration(response http.ResponseWriter, request *
response.Write(jsonReply) 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) { func (wfe *WebFrontEndImpl) Authorization(response http.ResponseWriter, request *http.Request) {
wfe.sendStandardHeaders(response) wfe.sendStandardHeaders(response)
@ -812,6 +824,8 @@ func (wfe *WebFrontEndImpl) Authorization(response http.ResponseWriter, request
var allHex = regexp.MustCompile("^[0-9a-f]+$") 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) { func (wfe *WebFrontEndImpl) Certificate(response http.ResponseWriter, request *http.Request) {
wfe.sendStandardHeaders(response) wfe.sendStandardHeaders(response)
@ -859,9 +873,15 @@ func (wfe *WebFrontEndImpl) Certificate(response http.ResponseWriter, request *h
if _, err = response.Write(cert.DER); err != nil { if _, err = response.Write(cert.DER); err != nil {
wfe.log.Warning(fmt.Sprintf("Could not write response: %s", err)) 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) { func (wfe *WebFrontEndImpl) Terms(response http.ResponseWriter, request *http.Request) {
wfe.sendStandardHeaders(response) 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") 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) { func (wfe *WebFrontEndImpl) Issuer(response http.ResponseWriter, request *http.Request) {
wfe.sendStandardHeaders(response) wfe.sendStandardHeaders(response)

View File

@ -710,7 +710,7 @@ func TestRevokeCertificate(t *testing.T) {
certBlock, _ := pem.Decode(certPemBytes) certBlock, _ := pem.Decode(certPemBytes)
test.Assert(t, certBlock != nil, "Failed to decode PEM") test.Assert(t, certBlock != nil, "Failed to decode PEM")
var revokeRequest struct { var revokeRequest struct {
CertificateDER core.JsonBuffer `json:"certificate"` CertificateDER core.JSONBuffer `json:"certificate"`
} }
revokeRequest.CertificateDER = certBlock.Bytes revokeRequest.CertificateDER = certBlock.Bytes
revokeRequestJSON, err := json.Marshal(revokeRequest) revokeRequestJSON, err := json.Marshal(revokeRequest)
@ -767,7 +767,7 @@ func TestRevokeCertificateAlreadyRevoked(t *testing.T) {
certBlock, _ := pem.Decode(certPemBytes) certBlock, _ := pem.Decode(certPemBytes)
test.Assert(t, certBlock != nil, "Failed to decode PEM") test.Assert(t, certBlock != nil, "Failed to decode PEM")
var revokeRequest struct { var revokeRequest struct {
CertificateDER core.JsonBuffer `json:"certificate"` CertificateDER core.JSONBuffer `json:"certificate"`
} }
revokeRequest.CertificateDER = certBlock.Bytes revokeRequest.CertificateDER = certBlock.Bytes
revokeRequestJSON, err := json.Marshal(revokeRequest) revokeRequestJSON, err := json.Marshal(revokeRequest)