Merge branch 'master' into mailer

This commit is contained in:
Roland Shoemaker 2015-07-24 16:36:50 -07:00
commit bd9286dd5b
32 changed files with 2744 additions and 387 deletions

26
Godeps/Godeps.json generated
View File

@ -12,51 +12,51 @@
},
{
"ImportPath": "github.com/cloudflare/cfssl/auth",
"Rev": "fd649feb9eb317e3ed24afee0d92c0ea55bf4a33"
"Rev": "e46a042fbff1afcb445a5164d392ab2bf1b938be"
},
{
"ImportPath": "github.com/cloudflare/cfssl/config",
"Rev": "fd649feb9eb317e3ed24afee0d92c0ea55bf4a33"
"Rev": "e46a042fbff1afcb445a5164d392ab2bf1b938be"
},
{
"ImportPath": "github.com/cloudflare/cfssl/crypto/pkcs11key",
"Rev": "fd649feb9eb317e3ed24afee0d92c0ea55bf4a33"
"Rev": "e46a042fbff1afcb445a5164d392ab2bf1b938be"
},
{
"ImportPath": "github.com/cloudflare/cfssl/crypto/pkcs12",
"Rev": "fd649feb9eb317e3ed24afee0d92c0ea55bf4a33"
"Rev": "e46a042fbff1afcb445a5164d392ab2bf1b938be"
},
{
"ImportPath": "github.com/cloudflare/cfssl/crypto/pkcs7",
"Rev": "fd649feb9eb317e3ed24afee0d92c0ea55bf4a33"
"Rev": "e46a042fbff1afcb445a5164d392ab2bf1b938be"
},
{
"ImportPath": "github.com/cloudflare/cfssl/csr",
"Rev": "fd649feb9eb317e3ed24afee0d92c0ea55bf4a33"
"Rev": "e46a042fbff1afcb445a5164d392ab2bf1b938be"
},
{
"ImportPath": "github.com/cloudflare/cfssl/errors",
"Rev": "fd649feb9eb317e3ed24afee0d92c0ea55bf4a33"
"Rev": "e46a042fbff1afcb445a5164d392ab2bf1b938be"
},
{
"ImportPath": "github.com/cloudflare/cfssl/helpers",
"Rev": "fd649feb9eb317e3ed24afee0d92c0ea55bf4a33"
"Rev": "e46a042fbff1afcb445a5164d392ab2bf1b938be"
},
{
"ImportPath": "github.com/cloudflare/cfssl/info",
"Rev": "fd649feb9eb317e3ed24afee0d92c0ea55bf4a33"
"Rev": "e46a042fbff1afcb445a5164d392ab2bf1b938be"
},
{
"ImportPath": "github.com/cloudflare/cfssl/log",
"Rev": "fd649feb9eb317e3ed24afee0d92c0ea55bf4a33"
"Rev": "e46a042fbff1afcb445a5164d392ab2bf1b938be"
},
{
"ImportPath": "github.com/cloudflare/cfssl/ocsp",
"Rev": "fd649feb9eb317e3ed24afee0d92c0ea55bf4a33"
"Rev": "e46a042fbff1afcb445a5164d392ab2bf1b938be"
},
{
"ImportPath": "github.com/cloudflare/cfssl/signer",
"Rev": "fd649feb9eb317e3ed24afee0d92c0ea55bf4a33"
"Rev": "e46a042fbff1afcb445a5164d392ab2bf1b938be"
},
{
"ImportPath": "github.com/codegangsta/cli",
@ -78,7 +78,7 @@
},
{
"ImportPath": "github.com/miekg/dns",
"Rev": "6da0cd2c927d5cb11255c468b5c3a1744c3351b1"
"Rev": "259969e797348d20e8c144a7573c23f06fa962f5"
},
{
"ImportPath": "github.com/miekg/pkcs11",

View File

@ -391,6 +391,7 @@ var KeyUsage = map[string]x509.KeyUsage{
"digital signature": x509.KeyUsageDigitalSignature,
"content committment": x509.KeyUsageContentCommitment,
"key encipherment": x509.KeyUsageKeyEncipherment,
"key agreement": x509.KeyUsageKeyAgreement,
"data encipherment": x509.KeyUsageDataEncipherment,
"cert sign": x509.KeyUsageCertSign,
"crl sign": x509.KeyUsageCRLSign,

View File

@ -1,4 +1,4 @@
// +build pkcs11
// +build !nopkcs11
// Package pkcs11key implements crypto.Signer for PKCS #11 private
// keys. Currently, only RSA keys are support.
@ -131,7 +131,10 @@ func New(module, slot, pin, privLabel string) (ps *PKCS11Key, err error) {
ps.Destroy()
return
}
ps.publicKey = rsa.PublicKey{n, e}
ps.publicKey = rsa.PublicKey{
N: n,
E: e,
}
return
}

View File

@ -1,8 +1,7 @@
// Package pkcs11 in the ocsp directory provides a way to construct a
// PKCS#11-based OCSP signer. It is only available in binaries built with the
// pkcs11 tag, i.e. `go build -tags pkcs11 ./cmd/cfssl`.
// +build pkcs11
// +build !nopkcs11
// Package pkcs11 in the ocsp directory provides a way to construct a
// PKCS#11-based OCSP signer.
package pkcs11
import (
@ -18,7 +17,7 @@ import (
// Enabled is set to true if PKCS #11 support is present.
const Enabled = true
// New returns a new PKCS #11 signer.
// NewPKCS11Signer returns a new PKCS #11 signer.
func NewPKCS11Signer(cfg ocspConfig.Config) (ocsp.Signer, error) {
log.Debugf("Loading PKCS #11 module %s", cfg.PKCS11.Module)
certData, err := ioutil.ReadFile(cfg.CACertFile)

View File

@ -1,4 +1,4 @@
// +build !pkcs11
// +build nopkcs11
package pkcs11

View File

@ -1,4 +1,4 @@
// +build pkcs11
// +build !nopkcs11
package pkcs11

View File

@ -1,4 +1,4 @@
// +build !pkcs11
// +build nopkcs11
package pkcs11

View File

@ -37,6 +37,7 @@ A not-so-up-to-date-list-that-may-be-actually-current:
* https://github.com/StalkR/dns-reverse-proxy
* https://github.com/tianon/rawdns
* https://mesosphere.github.io/mesos-dns/
* https://pulse.turbobytes.com/
Send pull request if you want to be listed here.

View File

@ -189,26 +189,15 @@ func (c *Client) exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err erro
// If the received message contains a TSIG record the transaction
// signature is verified.
func (co *Conn) ReadMsg() (*Msg, error) {
var p []byte
m := new(Msg)
if _, ok := co.Conn.(*net.TCPConn); ok {
p = make([]byte, MaxMsgSize)
} else {
if co.UDPSize > MinMsgSize {
p = make([]byte, co.UDPSize)
} else {
p = make([]byte, MinMsgSize)
}
}
n, err := co.Read(p)
if err != nil && n == 0 {
p, err := co.ReadMsgHeader(nil)
if err != nil {
return nil, err
}
p = p[:n]
m := new(Msg)
if err := m.Unpack(p); err != nil {
return nil, err
}
co.rtt = time.Since(co.t)
if t := m.IsTsig(); t != nil {
if _, ok := co.TsigSecret[t.Hdr.Name]; !ok {
return m, ErrSecret
@ -219,6 +208,81 @@ func (co *Conn) ReadMsg() (*Msg, error) {
return m, err
}
// ReadMsgHeader reads a DNS message, parses and populates hdr (when hdr is not nil).
// Returns message as a byte slice to be parsed with Msg.Unpack later on.
// Note that error handling on the message body is not possible as only the header is parsed.
func (co *Conn) ReadMsgHeader(hdr *Header) ([]byte, error) {
var (
p []byte
n int
err error
)
if t, ok := co.Conn.(*net.TCPConn); ok {
// First two bytes specify the length of the entire message.
l, err := tcpMsgLen(t)
if err != nil {
return nil, err
}
p = make([]byte, l)
n, err = tcpRead(t, p)
} else {
if co.UDPSize > MinMsgSize {
p = make([]byte, co.UDPSize)
} else {
p = make([]byte, MinMsgSize)
}
n, err = co.Read(p)
}
if err != nil {
return nil, err
} else if n < headerSize {
return nil, ErrShortRead
}
p = p[:n]
if hdr != nil {
if _, err = UnpackStruct(hdr, p, 0); err != nil {
return nil, err
}
}
return p, err
}
// tcpMsgLen is a helper func to read first two bytes of stream as uint16 packet length.
func tcpMsgLen(t *net.TCPConn) (int, error) {
p := []byte{0, 0}
n, err := t.Read(p)
if err != nil {
return 0, err
}
if n != 2 {
return 0, ErrShortRead
}
l, _ := unpackUint16(p, 0)
if l == 0 {
return 0, ErrShortRead
}
return int(l), nil
}
// tcpRead calls TCPConn.Read enough times to fill allocated buffer.
func tcpRead(t *net.TCPConn, p []byte) (int, error) {
n, err := t.Read(p)
if err != nil {
return n, err
}
for n < len(p) {
j, err := t.Read(p[n:])
if err != nil {
return n, err
}
n += j
}
return n, err
}
// Read implements the net.Conn read method.
func (co *Conn) Read(p []byte) (n int, err error) {
if co.Conn == nil {
@ -228,37 +292,22 @@ func (co *Conn) Read(p []byte) (n int, err error) {
return 0, io.ErrShortBuffer
}
if t, ok := co.Conn.(*net.TCPConn); ok {
n, err = t.Read(p[0:2])
if err != nil || n != 2 {
return n, err
l, err := tcpMsgLen(t)
if err != nil {
return 0, err
}
l, _ := unpackUint16(p[0:2], 0)
if l == 0 {
return 0, ErrShortRead
}
if int(l) > len(p) {
if l > len(p) {
return int(l), io.ErrShortBuffer
}
n, err = t.Read(p[:l])
if err != nil {
return n, err
}
i := n
for i < int(l) {
j, err := t.Read(p[i:int(l)])
if err != nil {
return i, err
}
i += j
}
n = i
return n, err
return tcpRead(t, p[:l])
}
// UDP connection
n, err = co.Conn.Read(p)
if err != nil {
return n, err
}
co.rtt = time.Since(co.t)
return n, err
}

View File

@ -32,7 +32,7 @@ func TestClientSync(t *testing.T) {
if err != nil {
t.Errorf("failed to exchange: %v", err)
}
if r != nil && r.Rcode != RcodeSuccess {
if r == nil || r.Rcode != RcodeSuccess {
t.Errorf("failed to get an valid answer\n%v", r)
}
}
@ -235,3 +235,52 @@ func ExampleUpdateLeaseTSIG(t *testing.T) {
t.Error(err)
}
}
func TestClientConn(t *testing.T) {
HandleFunc("miek.nl.", HelloServer)
defer HandleRemove("miek.nl.")
// This uses TCP just to make it slightly different than TestClientSync
s, addrstr, err := RunLocalTCPServer("127.0.0.1:0")
if err != nil {
t.Fatalf("Unable to run test server: %v", err)
}
defer s.Shutdown()
m := new(Msg)
m.SetQuestion("miek.nl.", TypeSOA)
cn, err := Dial("tcp", addrstr)
if err != nil {
t.Errorf("failed to dial %s: %v", addrstr, err)
}
err = cn.WriteMsg(m)
if err != nil {
t.Errorf("failed to exchange: %v", err)
}
r, err := cn.ReadMsg()
if r == nil || r.Rcode != RcodeSuccess {
t.Errorf("failed to get an valid answer\n%v", r)
}
err = cn.WriteMsg(m)
if err != nil {
t.Errorf("failed to exchange: %v", err)
}
h := new(Header)
buf, err := cn.ReadMsgHeader(h)
if buf == nil {
t.Errorf("failed to get an valid answer\n%v", r)
}
if int(h.Bits&0xF) != RcodeSuccess {
t.Errorf("failed to get an valid answer in ReadMsgHeader\n%v", r)
}
if h.Ancount != 0 || h.Qdcount != 1 || h.Nscount != 0 || h.Arcount != 1 {
t.Errorf("expected to have question and additional in response; got something else: %+v", h)
}
if err = r.Unpack(buf); err != nil {
t.Errorf("unable to unpack message fully: %v", err)
}
}

View File

@ -202,6 +202,9 @@ RFC 6895 sets aside a range of type codes for private use. This range
is 65,280 - 65,534 (0xFF00 - 0xFFFE). When experimenting with new Resource Records these
can be used, before requesting an official type code from IANA.
see http://miek.nl/posts/2014/Sep/21/Private%20RRs%20and%20IDN%20in%20Go%20DNS/ for more
information.
EDNS0
EDNS0 is an extension mechanism for the DNS defined in RFC 2671 and updated

File diff suppressed because it is too large Load Diff

View File

@ -3,9 +3,10 @@ package idn
import (
"bytes"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/miekg/dns"
"strings"
"unicode"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/miekg/dns"
)
// Implementation idea from RFC itself and from from IDNA::Punycode created by
@ -26,8 +27,8 @@ const (
)
// ToPunycode converts unicode domain names to DNS-appropriate punycode names.
// This function would return incorrect result for strings for non-canonical
// unicode strings.
// This function would return an empty string result for domain names with
// invalid unicode strings. This function expects domain names in lowercase.
func ToPunycode(s string) string {
tokens := dns.SplitDomainName(s)
switch {
@ -40,7 +41,11 @@ func ToPunycode(s string) string {
}
for i := range tokens {
tokens[i] = string(encode([]byte(tokens[i])))
t := encode([]byte(tokens[i]))
if t == nil {
return ""
}
tokens[i] = string(t)
}
return strings.Join(tokens, ".")
}
@ -138,12 +143,18 @@ func tfunc(k, bias rune) rune {
return k - bias
}
// encode transforms Unicode input bytes (that represent DNS label) into punycode bytestream
// encode transforms Unicode input bytes (that represent DNS label) into
// punycode bytestream. This function would return nil if there's an invalid
// character in the label.
func encode(input []byte) []byte {
n, bias := _N, _BIAS
b := bytes.Runes(input)
for i := range b {
if !isValidRune(b[i]) {
return nil
}
b[i] = preprune(b[i])
}
@ -267,3 +278,34 @@ func decode(b []byte) []byte {
}
return ret.Bytes()
}
// isValidRune checks if the character is valid. We will look for the
// character property in the code points list. For now we aren't checking special
// rules in case of contextual property
func isValidRune(r rune) bool {
return findProperty(r) == propertyPVALID
}
// findProperty will try to check the code point property of the given
// character. It will use a binary search algorithm as we have a slice of
// ordered ranges (average case performance O(log n))
func findProperty(r rune) property {
imin, imax := 0, len(codePoints)
for imax >= imin {
imid := (imin + imax) / 2
codePoint := codePoints[imid]
if (codePoint.start == r && codePoint.end == 0) || (codePoint.start <= r && codePoint.end >= r) {
return codePoint.state
}
if (codePoint.end > 0 && codePoint.end < r) || (codePoint.end == 0 && codePoint.start < r) {
imin = imid + 1
} else {
imax = imid - 1
}
}
return propertyUnknown
}

View File

@ -13,13 +13,13 @@ var testcases = [][2]string{
{"AbC", "abc"},
{"я", "xn--41a"},
{"zя", "xn--z-0ub"},
{"ЯZ", "xn--z-zub"},
{"яZ", "xn--z-zub"},
{"а-я", "xn----7sb8g"},
{"إختبار", "xn--kgbechtv"},
{"آزمایشی", "xn--hgbk6aj7f53bba"},
{"测试", "xn--0zwm56d"},
{"測試", "xn--g6w251d"},
{"Испытание", "xn--80akhbyknj4f"},
{"испытание", "xn--80akhbyknj4f"},
{"परीक्षा", "xn--11b5bs3a9aj6g"},
{"δοκιμή", "xn--jxalpdlp"},
{"테스트", "xn--9t4b11yi5a"},
@ -27,6 +27,7 @@ var testcases = [][2]string{
{"テスト", "xn--zckzah"},
{"பரிட்சை", "xn--hlcj6aya9esc7a"},
{"mamão-com-açúcar", "xn--mamo-com-acar-yeb1e6q"},
{"σ", "xn--4xa"},
}
func TestEncodeDecodePunycode(t *testing.T) {
@ -81,17 +82,34 @@ func TestEncodeDecodeFinalPeriod(t *testing.T) {
}
}
var invalid = []string{
var invalidACEs = []string{
"xn--*",
"xn--",
"xn---",
}
func TestInvalidPunycode(t *testing.T) {
for _, d := range invalid {
for _, d := range invalidACEs {
s := FromPunycode(d)
if s != d {
t.Errorf("Changed invalid name %s to %#v", d, s)
}
}
}
// You can verify the labels that are valid or not comparing to the Verisign
// website: http://mct.verisign-grs.com/
var invalidUnicodes = []string{
"Σ",
"ЯZ",
"Испытание",
}
func TestInvalidUnicodes(t *testing.T) {
for _, d := range invalidUnicodes {
s := ToPunycode(d)
if s != "" {
t.Errorf("Changed invalid name %s to %#v", d, s)
}
}
}

View File

@ -921,7 +921,7 @@ func packStructValue(val reflect.Value, msg []byte, off int, compression map[str
copy(msg[off:off+len(s)], s)
off += len(s)
case `dns:"octet"`:
bytesTmp := make([]byte, 256*4+1)
bytesTmp := make([]byte, 256)
off, err = packOctetString(fv.String(), msg, off, bytesTmp)
if err != nil {
return lenmsg, err

View File

@ -1477,3 +1477,32 @@ func TestParseCAA(t *testing.T) {
}
}
}
func TestPackCAA(t *testing.T) {
m := new(Msg)
record := new(CAA)
record.Hdr = RR_Header{Name: "example.com.", Rrtype: TypeCAA, Class: ClassINET, Ttl: 0}
record.Tag = "issue"
record.Value = "symantec.com"
record.Flag = 1
m.Answer = append(m.Answer, record)
bytes, err := m.Pack()
if err != nil {
t.Fatalf("failed to pack msg: %v", err)
}
if err := m.Unpack(bytes); err != nil {
t.Fatalf("failed to unpack msg: %v", err)
}
if len(m.Answer) != 1 {
t.Fatalf("incorrect number of answers unpacked")
}
rr := m.Answer[0].(*CAA)
if rr.Tag != "issue" {
t.Fatalf("invalid tag for unpacked answer")
} else if rr.Value != "symantec.com" {
t.Fatalf("invalid value for unpacked answer")
} else if rr.Flag != 1 {
t.Fatalf("invalid flag for unpacked answer")
}
}

View File

@ -158,6 +158,8 @@ type Header struct {
}
const (
headerSize = 12
// Header.Bits
_QR = 1 << 15 // query/response (response=1)
_AA = 1 << 10 // authoritative

View File

@ -39,9 +39,9 @@ func main() {
rai := ra.NewRegistrationAuthorityImpl()
rai.AuthzBase = c.Common.BaseURL + wfe.AuthzPath
rai.MaxKeySize = c.Common.MaxKeySize
raDNSTimeout, err := time.ParseDuration(c.RA.DNSTimeout)
raDNSTimeout, err := time.ParseDuration(c.Common.DNSTimeout)
cmd.FailOnError(err, "Couldn't parse RA DNS timeout")
rai.DNSResolver = core.NewDNSResolverImpl(raDNSTimeout, []string{c.RA.DNSResolver})
rai.DNSResolver = core.NewDNSResolverImpl(raDNSTimeout, []string{c.Common.DNSResolver})
go cmd.ProfileCmd("RA", stats)

View File

@ -38,9 +38,9 @@ func main() {
go cmd.ProfileCmd("VA", stats)
vai := va.NewValidationAuthorityImpl(c.CA.TestMode)
dnsTimeout, err := time.ParseDuration(c.VA.DNSTimeout)
dnsTimeout, err := time.ParseDuration(c.Common.DNSTimeout)
cmd.FailOnError(err, "Couldn't parse DNS timeout")
vai.DNSResolver = core.NewDNSResolverImpl(dnsTimeout, []string{c.VA.DNSResolver})
vai.DNSResolver = core.NewDNSResolverImpl(dnsTimeout, []string{c.Common.DNSResolver})
vai.UserAgent = c.VA.UserAgent
for {

View File

@ -91,15 +91,16 @@ func main() {
wfei.IssuerCacheDuration, err = time.ParseDuration(c.WFE.IssuerCacheDuration)
cmd.FailOnError(err, "Couldn't parse issuer caching duration")
dnsTimeout, err := time.ParseDuration(c.Common.DNSTimeout)
cmd.FailOnError(err, "Couldn't parse DNS timeout")
dnsResolver := core.NewDNSResolverImpl(dnsTimeout, []string{c.Common.DNSResolver})
ra := ra.NewRegistrationAuthorityImpl()
raDNSTimeout, err := time.ParseDuration(c.RA.DNSTimeout)
cmd.FailOnError(err, "Couldn't parse RA DNS timeout")
ra.DNSResolver = core.NewDNSResolverImpl(raDNSTimeout, []string{c.RA.DNSResolver})
ra.DNSResolver = dnsResolver
va := va.NewValidationAuthorityImpl(c.CA.TestMode)
vaDNSTimeout, err := time.ParseDuration(c.VA.DNSTimeout)
cmd.FailOnError(err, "Couldn't parse VA DNS timeout")
va.DNSResolver = core.NewDNSResolverImpl(vaDNSTimeout, []string{c.VA.DNSResolver})
va.DNSResolver = dnsResolver
va.UserAgent = c.VA.UserAgent
cadb, err := ca.NewCertificateAuthorityDatabaseImpl(c.CA.DBDriver, c.CA.DBConnect)

View File

@ -1,94 +0,0 @@
package main
import (
"crypto/rand"
"crypto/x509"
"crypto/x509/pkix"
"encoding/json"
"encoding/pem"
"flag"
"fmt"
"io/ioutil"
"time"
// "github.com/cloudflare/cfssl/crypto/pkcs11key"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/log"
)
var certFile = flag.String("ca", "", "JSON file for subject and validity")
var listFile = flag.String("revoked", "", "JSON file with a list of pkix.RevokedCertificate objects")
var module = flag.String("pkcs11-module", "", "PKCS#11 module")
var pin = flag.String("pkcs11-pin", "", "PKCS#11 password")
var token = flag.String("pkcs11-token", "", "PKCS#11 token name")
var label = flag.String("pkcs11-label", "", "PKCS#11 key label")
// Config defines the configuration loaded from listFile.
type Config struct {
ThisUpdate time.Time
NextUpdate time.Time
RevokedCerts []pkix.RevokedCertificate
}
func main() {
// Validate input
// All flags are required
flag.Parse()
missing := false
flag.VisitAll(func(f *flag.Flag) {
if len(f.Value.String()) == 0 {
missing = true
}
})
if missing {
log.Critical("All flags must be provided.")
flag.Usage()
return
}
// Read the issuer cert
certPEM, err := ioutil.ReadFile(*certFile)
if err != nil {
log.Criticalf("Unable to read certificate: %v", err)
return
}
certBlock, _ := pem.Decode(certPEM)
cert, err := x509.ParseCertificate(certBlock.Bytes)
if err != nil {
log.Criticalf("Unable to parse certificate: %v", err)
return
}
// Read the list of revoked certs
jsonConfig, err := ioutil.ReadFile(*listFile)
if err != nil {
log.Criticalf("Unable to read list of revoked certs: %v", err)
return
}
var config Config
err = json.Unmarshal(jsonConfig, &config)
if err != nil {
log.Criticalf("Unable to parse list of revoked certs: %v", err)
return
}
// Set up PKCS#11 key
priv, err := pkcs11key.New(*module, *token, *pin, *label)
if err != nil {
log.Criticalf("Unable to instantiate PKCS#11 private key: %v", err)
return
}
// Sign the CRL
crlDER, err := cert.CreateCRL(rand.Reader, priv, config.RevokedCerts, config.ThisUpdate, config.NextUpdate)
if err != nil {
log.Criticalf("Error signing certificate: %v", err)
return
}
fmt.Println(string(pem.EncodeToMemory(&pem.Block{
Type: "X509 CRL",
Bytes: crlDER,
})))
}

View File

@ -1,133 +0,0 @@
package main
import (
"crypto/rand"
"crypto/sha1"
"crypto/x509"
"crypto/x509/pkix"
"encoding/json"
"encoding/pem"
"flag"
"fmt"
"io/ioutil"
"math/big"
"time"
// "github.com/cloudflare/cfssl/crypto/pkcs11key"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/log"
)
var configFile = flag.String("config", "", "JSON file for subject and validity")
var module = flag.String("pkcs11-module", "", "PKCS#11 module")
var pin = flag.String("pkcs11-pin", "", "PKCS#11 password")
var token = flag.String("pkcs11-token", "", "PKCS#11 token name")
var label = flag.String("pkcs11-label", "", "PKCS#11 key label")
// Config defines the configuration loaded from configFile.
type Config struct {
Name struct {
C string
O string
OU string
CN string
}
NotBefore time.Time
NotAfter time.Time
}
func main() {
// Validate input
// All flags are required
flag.Parse()
missing := false
flag.VisitAll(func(f *flag.Flag) {
if len(f.Value.String()) == 0 {
missing = true
}
})
if missing {
log.Critical("All flags must be provided.")
flag.Usage()
return
}
jsonConfig, err := ioutil.ReadFile(*configFile)
if err != nil {
log.Criticalf("Unable to read config: %v", err)
return
}
var config Config
err = json.Unmarshal(jsonConfig, &config)
if err != nil {
log.Criticalf("Unable to parse config: %v", err)
return
}
if len(config.Name.C) == 0 || len(config.Name.O) == 0 ||
len(config.Name.CN) == 0 {
log.Criticalf("Config must provide country, organizationName, and commonName")
return
}
if config.NotBefore.After(config.NotAfter) {
log.Criticalf("Invalid validity: notAfter is before notBefore")
return
}
// Set up PKCS#11 key
priv, err := pkcs11key.New(*module, *token, *pin, *label)
if err != nil {
log.Criticalf("Unable to instantiate PKCS#11 private key: %v", err)
return
}
pub := priv.Public()
// Generate serial number
serialLimit := new(big.Int).Lsh(big.NewInt(1), 128)
serialNumber, err := rand.Int(rand.Reader, serialLimit)
if err != nil {
log.Criticalf("Error generating serial number: %v", err)
return
}
// Generate subject key ID
pubDER, err := x509.MarshalPKIXPublicKey(pub)
if err != nil {
log.Criticalf("Error serializing public key: %v", err)
return
}
h := sha1.New()
h.Write(pubDER)
keyID := h.Sum(nil)
// Sign the certificate
rootTemplate := &x509.Certificate{
SignatureAlgorithm: x509.SHA256WithRSA,
SerialNumber: serialNumber,
Subject: pkix.Name{
Country: []string{config.Name.C},
Organization: []string{config.Name.O},
CommonName: config.Name.CN,
},
NotBefore: config.NotBefore,
NotAfter: config.NotAfter,
BasicConstraintsValid: true,
IsCA: true,
SubjectKeyId: keyID,
}
rootDER, err := x509.CreateCertificate(rand.Reader, rootTemplate, rootTemplate, pub, priv)
if err != nil {
log.Criticalf("Error signing certificate: %v", err)
return
}
fmt.Println(string(pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE",
Bytes: rootDER,
})))
}

View File

@ -90,9 +90,6 @@ type Config struct {
}
RA struct {
DNSResolver string
DNSTimeout string
// DebugAddr is the address to run the /debug handlers on.
DebugAddr string
}
@ -106,9 +103,7 @@ type Config struct {
}
VA struct {
DNSResolver string
DNSTimeout string
UserAgent string
UserAgent string
// DebugAddr is the address to run the /debug handlers on.
DebugAddr string
@ -178,6 +173,9 @@ type Config struct {
// Path to a PEM-encoded copy of the issuer certificate.
IssuerCert string
MaxKeySize int
DNSResolver string
DNSTimeout string
}
SubscriberAgreementURL string

View File

@ -82,7 +82,7 @@ const (
)
func (pd *ProblemDetails) Error() string {
return fmt.Sprintf("%v :: %v", pd.Type, pd.Detail)
return fmt.Sprintf("%s :: %s", pd.Type, pd.Detail)
}
func cmpStrSlice(a, b []string) bool {
@ -145,13 +145,12 @@ type AcmeIdentifier struct {
// URIs pointing to authorizations that should collectively
// authorize the certificate being requsted.
//
// This type is never marshaled, since we only ever receive
// it from the client. So it carries some additional information
// that is useful internally. (We rely on Go's case-insensitive
// JSON unmarshal to properly unmarshal client requests.)
// This data is unmarshalled from JSON by way of rawCertificateRequest, which
// represents the actual structure received from the client.
type CertificateRequest struct {
CSR *x509.CertificateRequest // The CSR
Authorizations []AcmeURL // Links to Authorization over the account key
Bytes []byte // The original bytes of the CSR, for logging.
}
type rawCertificateRequest struct {
@ -173,6 +172,7 @@ func (cr *CertificateRequest) UnmarshalJSON(data []byte) error {
cr.CSR = csr
cr.Authorizations = raw.Authorizations
cr.Bytes = raw.CSR
return nil
}

View File

@ -183,7 +183,7 @@ func caller(level int) string {
func (log *AuditLogger) AuditPanic() {
if err := recover(); err != nil {
buf := make([]byte, 8192)
log.Audit(fmt.Sprintf("Panic caused by err: %v", err))
log.Audit(fmt.Sprintf("Panic caused by err: %s", err))
runtime.Stack(buf, false)
log.Audit(fmt.Sprintf("Stack Trace (Current frame) %s", buf))

View File

@ -153,7 +153,7 @@ func (ra *RegistrationAuthorityImpl) NewAuthorization(request core.Authorization
return authz, err
}
// AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c
ra.log.Audit(fmt.Sprintf("Checked CAA records for %s, registration ID %d [Present: %v, Valid for issuance: %v]", identifier.Value, regID, present, valid))
ra.log.Audit(fmt.Sprintf("Checked CAA records for %s, registration ID %d [Present: %t, Valid for issuance: %t]", identifier.Value, regID, present, valid))
if !valid {
err = errors.New("CAA check for identifier failed")
return authz, err

63
test.sh
View File

@ -26,7 +26,7 @@ TESTDIRS="analysis \
# Assume first it's the travis commit (for builds of master), unless we're
# a PR, when it's actually the first parent.
TRIGGER_COMMIT=${TRAVIS_COMMIT}
if [ "${TRAVIS_PULL_REQUEST}" != "false" ] ; then
if [ "x${TRAVIS_PULL_REQUEST}" != "x" ] ; then
revs=$(git rev-list --parents -n 1 HEAD)
# The trigger commit is the last ID in the space-delimited rev-list
TRIGGER_COMMIT=${revs##* }
@ -54,33 +54,33 @@ update_status() {
}
run() {
echo "$*"
$* 2>&1
echo "$@"
"$@" 2>&1
local status=$?
if [ ${status} -eq 0 ]; then
update_status --state success
echo "Success: $*"
echo "Success: $@"
else
FAILURE=1
update_status --state failure
echo "[!] FAILURE: $*"
echo "[!] FAILURE: $@"
fi
return ${status}
}
run_and_comment() {
if [ "x${TRAVIS}" = "x" ] || [ "${TRAVIS_PULL_REQUEST}" == "false" ] || [ ! -f "${GITHUB_SECRET_FILE}"] ; then
run $*
if [ "x${TRAVIS}" = "x" ] || [ "${TRAVIS_PULL_REQUEST}" == "false" ] || [ ! -f "${GITHUB_SECRET_FILE}" ] ; then
run "$@"
else
result=$(run $*)
result=$(run "$@")
local status=$?
# Only send a comment if exit code > 0
if [ ${status} -ne 0 ] ; then
echo $'```\n'${result}$'\n```' | github-pr-status --authfile $GITHUB_SECRET_FILE \
--owner "letsencrypt" --repo "boulder" \
comment --pr ${TRAVIS_PULL_REQUEST}
comment --pr "${TRAVIS_PULL_REQUEST}" -b -
fi
fi
}
@ -135,13 +135,7 @@ function run_unit_tests() {
[ -e $GOBIN/goveralls ] && $GOBIN/goveralls -coverprofile=gover.coverprofile -service=travis-ci
else
# Run all the tests together if local, for speed
dirlist=""
for dir in ${TESTDIRS}; do
dirlist="${dirlist} ./${dir}/"
done
run go test $GOTESTFLAGS -tags pkcs11 ${dirlist}
run go test $GOTESTFLAGS -tags pkcs11 ./...
fi
}
@ -169,29 +163,26 @@ end_context #test/golint
# Ensure all files are formatted per the `go fmt` tool
#
start_context "test/gofmt"
unformatted=$(find . -name "*.go" -not -path "./Godeps/*" -print | xargs -n1 gofmt -l)
if [ "x${unformatted}" == "x" ] ; then
update_status --state success
else
check_gofmt() {
unformatted=$(find . -name "*.go" -not -path "./Godeps/*" -print | xargs -n1 gofmt -l)
if [ "x${unformatted}" == "x" ] ; then
return 0
else
V="Unformatted files found.
Please run 'go fmt' on each of these files and amend your commit to continue."
V="Unformatted files found.
Please run 'go fmt' on each of these files and amend your commit to continue."
for f in ${unformatted}; do
V=$(printf "%s\n - %s" "${V}" "${f}")
done
for f in ${unformatted}; do
V=$(printf "%s\n - %s" "${V}" "${f}")
done
# Print to stdout
printf "%s\n\n" "${V}"
update_status --state failure --description "${V}"
# Post a comment with the unformatted list
if [ "${TRAVIS_PULL_REQUEST}" != "false" ] ; then
run_and_comment printf "%s\n\n" "${V}"
# Print to stdout
printf "%s\n\n" "${V}"
[ "${TRAVIS}" == "true" ] || exit 1 # Stop here if running locally
return 1
fi
}
[ "${TRAVIS}" == "true" ] || exit 1 # Stop here if running locally
fi
run_and_comment check_gofmt
end_context #test/gofmt
#
@ -211,7 +202,7 @@ fi
# If the unittests failed, exit before trying to run the integration test.
if [ ${FAILURE} != 0 ]; then
echo "--------------------------------------------------"
echo "--- A unit test failed. ---"
echo "--- A unit test or tool failed. ---"
echo "--- Stopping before running integration tests. ---"
echo "--------------------------------------------------"
exit ${FAILURE}

View File

@ -114,8 +114,6 @@
},
"ra": {
"dnsResolver": "8.8.8.8:53",
"dnsTimeout": "10s",
"debugAddr": "localhost:8002"
},
@ -126,8 +124,6 @@
},
"va": {
"dnsResolver": "8.8.8.8:53",
"dnsTimeout": "10s",
"userAgent": "boulder",
"debugAddr": "localhost:8004"
},
@ -177,7 +173,9 @@
"common": {
"baseURL": "http://localhost:4000",
"issuerCert": "test/test-ca.pem",
"maxKeySize": 4096
"maxKeySize": 4096,
"dnsResolver": "8.8.8.8:53",
"dnsTimeout": "10s"
},
"subscriberAgreementURL": "http://localhost:4300/terms"

View File

@ -101,8 +101,6 @@
},
"ra": {
"dnsResolver": "8.8.8.8:53",
"dnsTimeout": "10s",
"debugAddr": "localhost:8002"
},
@ -112,6 +110,11 @@
"debugAddr": "localhost:8003"
},
"va": {
"userAgent": "boulder",
"debugAddr": "localhost:8004"
},
"sql": {
"SQLDebug": true,
"CreateTables": true
@ -156,7 +159,9 @@
"common": {
"baseURL": "http://localhost:4000",
"issuerCert": "test/test-ca.pem"
"issuerCert": "test/test-ca.pem",
"dnsResolver": "8.8.8.8:53",
"dnsTimeout": "10s"
},
"subscriberAgreementURL": "http://localhost:4000/terms"

View File

@ -105,8 +105,6 @@
},
"ra": {
"dnsResolver": "127.0.0.1:8053",
"dnsTimeout": "10s",
"debugAddr": "localhost:8002"
},
@ -117,8 +115,6 @@
},
"va": {
"dnsResolver": "127.0.0.1:8053",
"dnsTimeout": "10s",
"userAgent": "boulder",
"debugAddr": "localhost:8004"
},
@ -144,7 +140,9 @@
"common": {
"baseURL": "http://localhost:4300",
"issuerCert": "test/test-ca.pem",
"maxKeySize": 4096
"maxKeySize": 4096,
"dnsResolver": "127.0.0.1:8053",
"dnsTimeout": "10s"
},
"subscriberAgreementURL": "http://example.com/terms"

View File

@ -43,7 +43,6 @@ const (
BuildIDPath = "/build"
)
// WebFrontEndImpl represents a Boulder web service and its resources
type WebFrontEndImpl struct {
RA core.RegistrationAuthority
SA core.StorageGetter
@ -289,15 +288,16 @@ func (wfe *WebFrontEndImpl) verifyPOST(request *http.Request, regCheck bool) ([]
return nil, nil, reg, errors.New("No body on POST")
}
body, err := ioutil.ReadAll(request.Body)
bodyBytes, err := ioutil.ReadAll(request.Body)
if err != nil {
return nil, nil, reg, err
}
body := string(bodyBytes)
// Parse as JWS
parsedJws, err := jose.ParseSigned(string(body))
parsedJws, err := jose.ParseSigned(body)
if err != nil {
wfe.log.Debug(fmt.Sprintf("Parse error reading JWS: %v", err))
wfe.log.Debug(fmt.Sprintf("Parse error reading JWS: %#v", err))
return nil, nil, reg, err
}
@ -312,7 +312,7 @@ func (wfe *WebFrontEndImpl) verifyPOST(request *http.Request, regCheck bool) ([]
return nil, nil, reg, errors.New("Too many signatures on POST")
}
if len(parsedJws.Signatures) == 0 {
wfe.log.Debug(fmt.Sprintf("POST not signed: %v", parsedJws))
wfe.log.Debug(fmt.Sprintf("POST not signed: %s", body))
return nil, nil, reg, errors.New("POST not signed")
}
key := parsedJws.Signatures[0].Header.JsonWebKey
@ -624,13 +624,26 @@ func (wfe *WebFrontEndImpl) RevokeCertificate(response http.ResponseWriter, requ
}
}
func (wfe *WebFrontEndImpl) logCsr(remoteAddr string, cr core.CertificateRequest, registration core.Registration) {
var csrLog = struct {
RemoteAddr string
CsrBase64 []byte
Registration core.Registration
}{
RemoteAddr: remoteAddr,
CsrBase64: cr.Bytes,
Registration: registration,
}
wfe.log.AuditObject("Certificate request", csrLog)
}
// 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) {
logEvent := wfe.populateRequestEvent(request)
defer wfe.logRequestDetails(&logEvent)
body, key, reg, err := wfe.verifyPOST(request, true)
body, _, reg, err := wfe.verifyPOST(request, true)
if err != nil {
logEvent.Error = err.Error()
respMsg := malformedJWS
@ -659,14 +672,12 @@ func (wfe *WebFrontEndImpl) NewCertificate(response http.ResponseWriter, request
wfe.sendError(response, "Error unmarshaling certificate request", err, http.StatusBadRequest)
return
}
wfe.logCsr(request.RemoteAddr, init, reg)
logEvent.Extra["Authorizations"] = init.Authorizations
logEvent.Extra["CSRDNSNames"] = init.CSR.DNSNames
logEvent.Extra["CSREmailAddresses"] = init.CSR.EmailAddresses
logEvent.Extra["CSRIPAddresses"] = init.CSR.IPAddresses
wfe.log.Notice(fmt.Sprintf("Client requested new certificate: %v %v %v",
request.RemoteAddr, init, key))
// Create new certificate and return
// TODO IMPORTANT: The RA trusts the WFE to provide the correct key. If the
// WFE is compromised, *and* the attacker knows the public key of an account

View File

@ -14,6 +14,7 @@ import (
"encoding/json"
"encoding/pem"
"errors"
"fmt"
"io"
"io/ioutil"
"log/syslog"
@ -128,8 +129,6 @@ wk6Oiadty3eQqSBJv0HnpmiEdQVffIK5Pg4M8Dd+aOBnEkbopAJOuA==
"5dd9c885526136d810fc7640f5ba56281e2b75fa3ff7c91a7d23bab7fd4"
)
var log = mocks.UseMockLog()
type MockSA struct {
// empty
}
@ -166,8 +165,8 @@ func (sa *MockSA) GetRegistrationByKey(jwk jose.JsonWebKey) (core.Registration,
return core.Registration{ID: 2}, sql.ErrNoRows
}
// Return a fake registration
return core.Registration{ID: 1, Agreement: agreementURL}, nil
// Return a fake registration. Make sure to fill the key field to avoid marshaling errors.
return core.Registration{ID: 1, Key: test1KeyPublic, Agreement: agreementURL}, nil
}
func (sa *MockSA) GetAuthorization(id string) (core.Authorization, error) {
@ -332,6 +331,7 @@ func setupWFE(t *testing.T) WebFrontEndImpl {
wfe.NewCert = wfe.BaseURL + NewCertPath
wfe.CertBase = wfe.BaseURL + CertPath
wfe.SubscriberAgreementURL = agreementURL
wfe.log.SyslogWriter = mocks.NewSyslogWriter()
return wfe
}
@ -496,6 +496,7 @@ func TestIssueCertificate(t *testing.T) {
wfe := setupWFE(t)
mux, err := wfe.Handler()
test.AssertNotError(t, err, "Problem setting up HTTP handlers")
mockLog := wfe.log.SyslogWriter.(*mocks.MockSyslogWriter)
// TODO: Use a mock RA so we can test various conditions of authorized, not authorized, etc.
ra := ra.NewRegistrationAuthorityImpl()
@ -572,9 +573,11 @@ func TestIssueCertificate(t *testing.T) {
"{\"type\":\"urn:acme:error:unauthorized\",\"detail\":\"Error creating new cert :: Invalid signature on CSR\"}")
// Valid, signed JWS body, payload has a CSR with no DNS names
mockLog.Clear()
responseWriter.Body.Reset()
wfe.NewCertificate(responseWriter, &http.Request{
Method: "POST",
Method: "POST",
RemoteAddr: "1.1.1.1",
Body: makeBody(signRequest(t, `{
"authorizations": [],
"csr": "MIIBBTCBsgIBADBNMQowCAYDVQQGEwFjMQowCAYDVQQKEwFvMQswCQYDVQQLEwJvdTEKMAgGA1UEBxMBbDEKMAgGA1UECBMBczEOMAwGA1UEAxMFT2ggaGkwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAsr76ZkU2RTqi41eHfmpE5htDvkr202yjRS8x2M5yzT52ooT2WEVtnSuim0YfOEw6f-fHmbqsasqKmqlsJdgz2QIDAQABoAAwCwYJKoZIhvcNAQEFA0EAHkCv4kVPJa53ltOGrhpdH0mT04qHUqiTllJPPjxXxn6iwiVYL8nQuhs4Q2758ENoODBuM2F8gH19TIoXlcm3LQ=="
@ -583,15 +586,18 @@ func TestIssueCertificate(t *testing.T) {
test.AssertEquals(t,
responseWriter.Body.String(),
"{\"type\":\"urn:acme:error:unauthorized\",\"detail\":\"Error creating new cert :: Key not authorized for name Oh hi\"}")
assertCsrLogged(t, mockLog)
// Valid, signed JWS body, payload has a valid CSR but no authorizations:
// {
// "csr": "MIIBK...",
// "authorizations: []
// }
mockLog.Clear()
responseWriter.Body.Reset()
wfe.NewCertificate(responseWriter, &http.Request{
Method: "POST",
Method: "POST",
RemoteAddr: "1.1.1.1",
Body: makeBody(signRequest(t, `{
"authorizations": [],
"csr": "MIIBKzCB2AIBADBNMQowCAYDVQQGEwFjMQowCAYDVQQKEwFvMQswCQYDVQQLEwJvdTEKMAgGA1UEBxMBbDEKMAgGA1UECBMBczEOMAwGA1UEAxMFT2ggaGkwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAqvFEGBNrjAotPbcdTSyDpxsESN0-eYl4TqS0ZLYwLTV-FuPHTPjFiq2oH1BEgmRzjb8YiPVXFMnaOeHE7zuuXQIDAQABoCYwJAYJKoZIhvcNAQkOMRcwFTATBgNVHREEDDAKgghtZWVwLmNvbTALBgkqhkiG9w0BAQUDQQBSEcEq-lMUnzv1DO8jK0hJR8YKc0yV8zuWVfAWN0_dsPg5Ny-OHhtJcOTIrUrLTb_xCU7cjiKxU8i3j1kaT-rt"
@ -600,8 +606,9 @@ func TestIssueCertificate(t *testing.T) {
test.AssertEquals(t,
responseWriter.Body.String(),
"{\"type\":\"urn:acme:error:unauthorized\",\"detail\":\"Error creating new cert :: Key not authorized for name meep.com\"}")
assertCsrLogged(t, mockLog)
log.Clear()
mockLog.Clear()
responseWriter.Body.Reset()
wfe.NewCertificate(responseWriter, &http.Request{
Method: "POST",
@ -610,6 +617,7 @@ func TestIssueCertificate(t *testing.T) {
"authorizations": ["valid"]
}`, &wfe.nonceService)),
})
assertCsrLogged(t, mockLog)
randomCertDer, _ := hex.DecodeString(GoodTestCert)
test.AssertEquals(t,
responseWriter.Body.String(),
@ -623,7 +631,7 @@ func TestIssueCertificate(t *testing.T) {
test.AssertEquals(
t, responseWriter.Header().Get("Content-Type"),
"application/pkix-cert")
reqlogs := log.GetAllMatching(`Certificate request - successful`)
reqlogs := mockLog.GetAllMatching(`Certificate request - successful`)
test.AssertEquals(t, len(reqlogs), 1)
test.AssertEquals(t, reqlogs[0].Priority, syslog.LOG_NOTICE)
test.AssertContains(t, reqlogs[0].Message, `[AUDIT] `)
@ -1206,5 +1214,37 @@ func TestGetCertificate(t *testing.T) {
test.AssertEquals(t, responseWriter.Code, 404)
test.AssertEquals(t, responseWriter.Header().Get("Cache-Control"), "public, max-age=0, no-cache")
test.AssertEquals(t, responseWriter.Body.String(), `{"type":"urn:acme:error:malformed","detail":"Certificate not found"}`)
}
func assertCsrLogged(t *testing.T, mockLog *mocks.MockSyslogWriter) {
matches := mockLog.GetAllMatching("^\\[AUDIT\\] Certificate request JSON=")
test.Assert(t, len(matches) == 1,
fmt.Sprintf("Incorrect number of certificate request log entries: %d",
len(matches)))
test.AssertEquals(t, matches[0].Priority, syslog.LOG_NOTICE)
}
func TestLogCsrPem(t *testing.T) {
const certificateRequestJson = `{
"csr": "MIICWTCCAUECAQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAycX3ca-fViOuRWF38mssORISFxbJvspDfhPGRBZDxJ63NIqQzupB-6dp48xkcX7Z_KDaRJStcpJT2S0u33moNT4FHLklQBETLhExDk66cmlz6Xibp3LGZAwhWuec7wJoEwIgY8oq4rxihIyGq7HVIJoq9DqZGrUgfZMDeEJqbphukQOaXGEop7mD-eeu8-z5EVkB1LiJ6Yej6R8MAhVPHzG5fyOu6YVo6vY6QgwjRLfZHNj5XthxgPIEETZlUbiSoI6J19GYHvLURBTy5Ys54lYAPIGfNwcIBAH4gtH9FrYcDY68R22rp4iuxdvkf03ZWiT0F2W1y7_C9B2jayTzvQIDAQABoAAwDQYJKoZIhvcNAQELBQADggEBAHd6Do9DIZ2hvdt1GwBXYjsqprZidT_DYOMfYcK17KlvdkFT58XrBH88ulLZ72NXEpiFMeTyzfs3XEyGq_Bbe7TBGVYZabUEh-LOskYwhgcOuThVN7tHnH5rhN-gb7cEdysjTb1QL-vOUwYgV75CB6PE5JVYK-cQsMIVvo0Kz4TpNgjJnWzbcH7h0mtvub-fCv92vBPjvYq8gUDLNrok6rbg05tdOJkXsF2G_W-Q6sf2Fvx0bK5JeH4an7P7cXF9VG9nd4sRt5zd-L3IcyvHVKxNhIJXZVH0AOqh_1YrKI9R0QKQiZCEy0xN1okPlcaIVaFhb7IKAHPxTI3r5f72LXY"
}`
wfe := setupWFE(t)
var certificateRequest core.CertificateRequest
err := json.Unmarshal([]byte(certificateRequestJson), &certificateRequest)
test.AssertNotError(t, err, "Unable to parse certificateRequest")
mockSA := MockSA{}
reg, err := mockSA.GetRegistration(789)
test.AssertNotError(t, err, "Unable to get registration")
remoteAddr := "12.34.98.76"
wfe.logCsr(remoteAddr, certificateRequest, reg)
mockLog := wfe.log.SyslogWriter.(*mocks.MockSyslogWriter)
matches := mockLog.GetAllMatching("Certificate request")
test.Assert(t, len(matches) == 1,
"Incorrect number of certificate request log entries")
test.AssertEquals(t, matches[0].Priority, syslog.LOG_NOTICE)
test.AssertEquals(t, matches[0].Message, `[AUDIT] Certificate request JSON={"RemoteAddr":"12.34.98.76","CsrBase64":"MIICWTCCAUECAQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAycX3ca+fViOuRWF38mssORISFxbJvspDfhPGRBZDxJ63NIqQzupB+6dp48xkcX7Z/KDaRJStcpJT2S0u33moNT4FHLklQBETLhExDk66cmlz6Xibp3LGZAwhWuec7wJoEwIgY8oq4rxihIyGq7HVIJoq9DqZGrUgfZMDeEJqbphukQOaXGEop7mD+eeu8+z5EVkB1LiJ6Yej6R8MAhVPHzG5fyOu6YVo6vY6QgwjRLfZHNj5XthxgPIEETZlUbiSoI6J19GYHvLURBTy5Ys54lYAPIGfNwcIBAH4gtH9FrYcDY68R22rp4iuxdvkf03ZWiT0F2W1y7/C9B2jayTzvQIDAQABoAAwDQYJKoZIhvcNAQELBQADggEBAHd6Do9DIZ2hvdt1GwBXYjsqprZidT/DYOMfYcK17KlvdkFT58XrBH88ulLZ72NXEpiFMeTyzfs3XEyGq/Bbe7TBGVYZabUEh+LOskYwhgcOuThVN7tHnH5rhN+gb7cEdysjTb1QL+vOUwYgV75CB6PE5JVYK+cQsMIVvo0Kz4TpNgjJnWzbcH7h0mtvub+fCv92vBPjvYq8gUDLNrok6rbg05tdOJkXsF2G/W+Q6sf2Fvx0bK5JeH4an7P7cXF9VG9nd4sRt5zd+L3IcyvHVKxNhIJXZVH0AOqh/1YrKI9R0QKQiZCEy0xN1okPlcaIVaFhb7IKAHPxTI3r5f72LXY=","Registration":{"id":789,"key":{"kty":"RSA","n":"yNWVhtYEKJR21y9xsHV-PD_bYwbXSeNuFal46xYxVfRL5mqha7vttvjB_vc7Xg2RvgCxHPCqoxgMPTzHrZT75LjCwIW2K_klBYN8oYvTwwmeSkAz6ut7ZxPv-nZaT5TJhGk0NT2kh_zSpdriEJ_3vW-mqxYbbBmpvHqsa1_zx9fSuHYctAZJWzxzUZXykbWMWQZpEiE0J4ajj51fInEzVn7VxV-mzfMyboQjujPh7aNJxAWSq4oQEJJDgWwSh9leyoJoPpONHxh5nEE5AjE01FkGICSxjpZsF-w8hOTI3XXohUdu29Se26k2B0PolDSuj0GIQU6-W9TdLXSjBb2SpQ","e":"AAEAAQ"},"recoveryToken":"","agreement":"http://example.invalid/terms"}}`)
}