Correct most `go lint` warnings. (274 -> 5)
This commit is contained in:
parent
383885df08
commit
41f5788c77
|
@ -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")
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 == "" {
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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...)
|
||||||
|
|
|
@ -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...)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.")
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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{}{}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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"`
|
||||||
|
|
||||||
|
|
|
@ -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":"!!!!"}`)
|
||||||
|
|
51
core/util.go
51
core/util.go
|
@ -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 {
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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" +
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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{},
|
||||||
|
|
|
@ -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(®, "SELECT * FROM registrations WHERE jwk = :key", map[string]interface{}{"key": string(keyJson)})
|
err = ssa.dbMap.SelectOne(®, "SELECT * FROM registrations WHERE jwk = :key", map[string]interface{}{"key": string(keyJSON)})
|
||||||
return
|
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)
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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",
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue