Commit d1543647 authored by matthias.lang's avatar matthias.lang
Browse files

Merge branch 'master' of git.scc.kit.edu:KIT-CA/websearch

* 'master' of git.scc.kit.edu:KIT-CA/websearch:
  Added performance test used for email index design
  removed old comments and code
  Implemented first version of emailtocert-API
  Added filtering for results
  Added first prototype for emailIndex and /mailtocert-Handler
parents aced3ddb aee602d6
......@@ -16,6 +16,7 @@ import (
"path/filepath"
"sort"
"strings"
"time"
)
const (
......@@ -137,9 +138,18 @@ func downloadHandler(w http.ResponseWriter, r *http.Request) {
}
}
func mailtocertHandler(w http.ResponseWriter, r *http.Request) {
//serial := mux.Vars(r)["serial"]
http.Error(w, "not implemented", http.StatusBadRequest)
func emailtocertHandler(w http.ResponseWriter, r *http.Request) {
email := mux.Vars(r)["email"]
// retrieve all certs with that email
results := ccache.IndexEmail.Get(email)
// only valid certs
results = results.Filter(
func(c *SearchableCert) bool {
return AllWatchers[WatchValid].Is(c.Serial, Valid) && c.NotAfter.After(time.Now())
})
w.Header().Set("cache-control", "no-store")
w.Header().Set("Content-Type", "application/json")
w.Write(results.JSONString(AllWatchers))
}
func main() {
......@@ -153,9 +163,7 @@ func main() {
log.Println(ccache.Len(), "certificates have been loaded into the certificate cache.")
// handle Validity change
//log.Println("Starting filewatcher for validity changes")
AllWatchers = CreateAllWatchers(certRepoDir)
//log.Printf("Read %d initial validity states", ValidityWatcher.Len())
// create http interface
r := mux.NewRouter()
......@@ -182,10 +190,10 @@ func main() {
Methods("GET").
HandlerFunc(downloadHandler)
// add mailtocert handler
r.Path("/mailtocert/v1/{serial:[0-9]+}").
// add emailtocert handler
r.Path("/emailtocert/v1/{email:.*@.*}").
Methods("GET").
HandlerFunc(mailtocertHandler)
HandlerFunc(emailtocertHandler)
// add request dumping handler
r.Path("/dumpreq").
......@@ -203,7 +211,7 @@ func main() {
r.PathPrefix("/").
Handler(http.FileServer(http.Dir(webrootDir)))
// DEBUG: add notfound handler
// DEBUG: add notfound handler
r.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
requestDump, err := httputil.DumpRequest(r, true)
if err != nil {
......
......@@ -2,8 +2,7 @@ package websearch
import (
"crypto/x509"
_ "encoding/gob"
_ "log"
"strings"
"sync"
)
......@@ -11,6 +10,7 @@ import (
type CertCache struct {
certs map[string]*SearchableCert
sync.RWMutex
IndexEmail CertIndex
}
// SCFilter functions implement a selection criteria for a SearchableCert
......@@ -18,7 +18,8 @@ type SCFilter func(*SearchableCert) bool
func NewCertCache() CertCache {
return CertCache{
certs: make(map[string]*SearchableCert),
certs: make(map[string]*SearchableCert),
IndexEmail: NewCertIndex(),
}
}
......@@ -32,6 +33,13 @@ func (cc *CertCache) Add(cert *x509.Certificate) (*SearchableCert, bool) {
cc.certs[searchableCert.Serial] = &searchableCert
cc.Unlock()
// add certificate to email index
for _, email := range searchableCert.EmailAddresses {
if FilterEncryptionEmail(&searchableCert) {
cc.IndexEmail.Add(strings.ToLower(email), &searchableCert)
}
}
return &searchableCert, isPresent
}
......@@ -64,7 +72,7 @@ func (cc *CertCache) Get(serial string) *SearchableCert {
// Filter returns all SearchableCerts that match the filter's criteria
func (cc *CertCache) Filter(filter SCFilter) Searchresults {
var matches []*SearchableCert
var matches Searchresults
cc.RLock()
for _, c := range cc.certs {
......
package websearch
import (
"sync"
)
// CertIndex provides a mapping between a string and a set of
// certificates in a thread-safe way
type CertIndex struct {
certs map[string]map[string]*SearchableCert
sync.RWMutex
}
func NewCertIndex() CertIndex {
return CertIndex{
certs: make(map[string]map[string]*SearchableCert),
}
}
// Add cert to the set which is associated with key
func (ci *CertIndex) Add(key string, cert *SearchableCert) {
ci.Lock()
keyMap, present := ci.certs[key]
if present {
// assume Serial is unique
keyMap[cert.Serial] = cert
} else {
ci.certs[key] = map[string]*SearchableCert{
cert.Serial: cert,
}
}
ci.Unlock()
}
// Get returns an array of all certificates for a key
func (ci *CertIndex) Get(key string) Searchresults {
var matches Searchresults
ci.RLock()
serialMap, present := ci.certs[key]
if present {
matches = make(Searchresults, len(serialMap))
var idx = 0
for _, c := range serialMap {
matches[idx] = c
idx++
}
} else {
matches = make(Searchresults, 0)
}
ci.RUnlock()
return matches
}
......@@ -57,6 +57,19 @@ func (r Searchresults) Less(i, j int) bool {
}
}
// Filter returns the subset of results that match filter
func (r Searchresults) Filter(filter SCFilter) Searchresults {
filtered := make(Searchresults, len(r))
matches := 0
for _, cert := range r {
if filter(cert) {
filtered[matches] = cert
matches++
}
}
return filtered[:matches]
}
func (r Searchresults) JSONString(watchers map[int]*AttributeState) []byte {
result := JSONShell{
Results: make([]*JSONResult, len(r)),
......
......@@ -280,6 +280,32 @@ func MakePublicSearchFilter(query string, visibilityWatcher *AttributeState) SCF
}
}
func FilterEncryptionEmail(c *SearchableCert) bool {
// only match certificates that contain at least one email address
if len(c.EmailAddresses) < 1 {
return false
}
// check if the KeyEncipherment KeyUsage bit is set
if c.rawCertificate.KeyUsage&x509.KeyUsageKeyEncipherment != x509.KeyUsageKeyEncipherment {
return false
}
// check if ExtendedKeyUsage contains EmailProtection
foundEmailProtection := false
for _, extension := range c.rawCertificate.ExtKeyUsage {
if extension == x509.ExtKeyUsageEmailProtection {
foundEmailProtection = true
}
}
if !foundEmailProtection {
return false
}
// filter RA-Ops
if strings.HasPrefix(c.rawCertificate.Subject.CommonName, "PN") && (strings.Contains(c.rawCertificate.Subject.CommonName, "Teilnehmerservice") || strings.HasSuffix(c.rawCertificate.Subject.CommonName, "RA-Operator")) {
return false
}
return true
}
// DnToString turns a DistinguishedName into a readable string
// According to the relevant RFCs, there is no canonical form.
// The returned format is designed to be sortable and have good
......@@ -458,7 +484,7 @@ func (c *SearchableCert) JSONResult(watchers map[int]*AttributeState) *JSONResul
CAGeneration: *c.CAGeneration,
Type: TypeToName(c.Type),
Profile: "",
Expired: c.NotAfter.After(time.Now()),
Expired: c.NotAfter.Before(time.Now()),
Validity: ValidityToName(watchers[WatchValid].Get(c.Serial)),
Public: VisibilityToName(watchers[WatchVisibile].Get(c.Serial)),
}
......
package heiko
import (
"testing"
)
/* Question: when building an array of unknown size (upper
bound known), how expensive (in terms of computation time)
isappend()ing every insert vs. pre-allocation plus truncating?
Answer: Approx. 2.5x.
*/
func BenchmarkPre1(b *testing.B) { benchmarkPre(1024, b) }
func BenchmarkPre2(b *testing.B) { benchmarkPre(65536, b) }
func BenchmarkPre3(b *testing.B) { benchmarkPre(16777216, b) }
func BenchmarkAppend1(b *testing.B) { benchmarkAppend(1024, b) }
func BenchmarkAppend2(b *testing.B) { benchmarkAppend(65536, b) }
func BenchmarkAppend3(b *testing.B) { benchmarkAppend(16777216, b) }
func benchmarkPre(size int, b *testing.B) {
array := AllocateTestArray(size)
for n := 0; n < b.N; n++ {
Preallocate(*array)
}
}
func benchmarkAppend(size int, b *testing.B) {
array := AllocateTestArray(size)
for n := 0; n < b.N; n++ {
Append(*array)
}
}
func Preallocate(source []int) {
dest := make([]int, len(source))
for i := 0; i < len(source); i++ {
dest[i] = source[i]
}
}
func Append(source []int) {
var dest []int
for i := 0; i < len(source); i++ {
dest = append(dest, source[i])
}
}
func AllocateTestArray(size int) *[]int {
array := make([]int, size)
for idx, _ := range array {
array[idx] = idx
}
return &array
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment