github.com/mre-fog/trillianxx@v1.1.2-0.20180615153820-ae375a99d36a/examples/ct/ctmapper/mapper/mapper.go (about)

     1  // Copyright 2016 Google Inc. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // The mapper binary performs log->map mapping.
    16  package main
    17  
    18  import (
    19  	"context"
    20  	"flag"
    21  	"fmt"
    22  	"time"
    23  
    24  	"github.com/golang/glog"
    25  	"github.com/golang/protobuf/proto"
    26  	"github.com/google/certificate-transparency-go/client"
    27  	"github.com/google/certificate-transparency-go/jsonclient"
    28  	"github.com/google/certificate-transparency-go/x509"
    29  	"github.com/google/trillian"
    30  	"github.com/google/trillian/examples/ct/ctmapper"
    31  	"github.com/google/trillian/examples/ct/ctmapper/ctmapperpb"
    32  	"github.com/google/trillian/types"
    33  	"google.golang.org/grpc"
    34  
    35  	pb "github.com/golang/protobuf/proto"
    36  	ct "github.com/google/certificate-transparency-go"
    37  )
    38  
    39  var sourceLog = flag.String("source", "https://ct.googleapis.com/submariner", "URL of the CT Log")
    40  var mapServer = flag.String("map_server", "", "host:port for the map server")
    41  var mapID = flag.Int("map_id", -1, "Map ID to write to")
    42  var logBatchSize = flag.Int("log_batch_size", 256, "Max number of entries to process at a time from the CT Log")
    43  
    44  //TODO(al): factor this out into a reusable thing.
    45  
    46  // CTMapper converts between a certificate transparency Log and a Trillian Map.
    47  type CTMapper struct {
    48  	mapID int64
    49  	ct    *client.LogClient
    50  	vmap  trillian.TrillianMapClient
    51  }
    52  
    53  func updateDomainMap(m map[string]ctmapperpb.EntryList, cert x509.Certificate, index int64, isPrecert bool) {
    54  	domains := make(map[string]bool)
    55  	if len(cert.Subject.CommonName) > 0 {
    56  		domains[cert.Subject.CommonName] = true
    57  	}
    58  	for _, n := range cert.DNSNames {
    59  		if len(n) > 0 {
    60  			domains[n] = true
    61  		}
    62  	}
    63  
    64  	for k := range domains {
    65  		el := m[k]
    66  		if isPrecert {
    67  			el.PrecertIndex = append(el.PrecertIndex, index)
    68  		} else {
    69  			el.CertIndex = append(el.CertIndex, index)
    70  		}
    71  		el.Domain = k
    72  		m[k] = el
    73  	}
    74  }
    75  
    76  func (m *CTMapper) oneMapperRun(ctx context.Context) (bool, error) {
    77  	start := time.Now()
    78  	glog.Info("starting mapping batch")
    79  	getRootReq := &trillian.GetSignedMapRootRequest{MapId: m.mapID}
    80  	getRootResp, err := m.vmap.GetSignedMapRoot(context.Background(), getRootReq)
    81  	if err != nil {
    82  		return false, err
    83  	}
    84  
    85  	var mapRoot types.MapRootV1
    86  	if err := mapRoot.UnmarshalBinary(getRootResp.GetMapRoot().GetMapRoot()); err != nil {
    87  		return false, err
    88  	}
    89  
    90  	mapperMetadata := &ctmapperpb.MapperMetadata{}
    91  	if err := proto.Unmarshal(mapRoot.Metadata, mapperMetadata); err != nil {
    92  		return false, fmt.Errorf("failed to unmarshal MapRoot.Metadata: %v", err)
    93  	}
    94  
    95  	startEntry := mapperMetadata.HighestFullyCompletedSeq + 1
    96  	endEntry := startEntry + int64(*logBatchSize)
    97  
    98  	glog.Infof("Fetching entries [%d, %d] from log", startEntry, endEntry)
    99  
   100  	// Get the entries from the log:
   101  	logEntries, err := m.ct.GetEntries(ctx, startEntry, endEntry)
   102  	if err != nil {
   103  		return false, err
   104  	}
   105  	if len(logEntries) == 0 {
   106  		glog.Info("No entries from log")
   107  		return false, nil
   108  	}
   109  
   110  	// figure out which domains we've found:
   111  	domains := make(map[string]ctmapperpb.EntryList)
   112  	for _, entry := range logEntries {
   113  		if entry.Leaf.LeafType != ct.TimestampedEntryLeafType {
   114  			glog.Info("Skipping unknown entry type %v at %d", entry.Leaf.LeafType, entry.Index)
   115  			continue
   116  		}
   117  		if entry.Index > mapperMetadata.HighestFullyCompletedSeq {
   118  			mapperMetadata.HighestFullyCompletedSeq = entry.Index
   119  		}
   120  		switch entry.Leaf.TimestampedEntry.EntryType {
   121  		case ct.X509LogEntryType:
   122  			cert, err := x509.ParseCertificate(entry.Leaf.TimestampedEntry.X509Entry.Data)
   123  			if err != nil {
   124  				glog.Warningf("Can't parse cert at index %d, continuing anyway because this is a toy", entry.Index)
   125  				continue
   126  			}
   127  			updateDomainMap(domains, *cert, entry.Index, false)
   128  		case ct.PrecertLogEntryType:
   129  			precert, err := x509.ParseTBSCertificate(entry.Leaf.TimestampedEntry.PrecertEntry.TBSCertificate)
   130  			if err != nil {
   131  				glog.Warningf("Can't parse precert at index %d, continuing anyway because this is a toy", entry.Index)
   132  				continue
   133  			}
   134  			updateDomainMap(domains, *precert, entry.Index, true)
   135  		default:
   136  			glog.Infof("Unknown logentry type at index %d", entry.Index)
   137  			continue
   138  		}
   139  	}
   140  
   141  	glog.Infof("Found %d unique domains from certs", len(domains))
   142  	glog.Info("Fetching current map values for domains...")
   143  
   144  	// Fetch the current map values for those domains:
   145  	getReq := &trillian.GetMapLeavesRequest{
   146  		MapId: m.mapID,
   147  		Index: make([][]byte, 0, len(domains)),
   148  	}
   149  	for d := range domains {
   150  		getReq.Index = append(getReq.Index, ctmapper.HashDomain(d))
   151  	}
   152  
   153  	getResp, err := m.vmap.GetLeaves(context.Background(), getReq)
   154  	if err != nil {
   155  		return false, err
   156  	}
   157  	//glog.Info("Get resp: %v", getResp)
   158  
   159  	proofs := 0
   160  	for _, v := range getResp.MapLeafInclusion {
   161  		e := ctmapperpb.EntryList{}
   162  		if len(v.Inclusion) > 0 {
   163  			proofs++
   164  		}
   165  		if err := pb.Unmarshal(v.Leaf.LeafValue, &e); err != nil {
   166  			return false, err
   167  		}
   168  		glog.Infof("Got %#v", e)
   169  		el := domains[e.Domain]
   170  		pb.Merge(&el, &e)
   171  		domains[e.Domain] = el
   172  		glog.Infof("will update for %s", e.Domain)
   173  	}
   174  	glog.Infof("Got %d values, and %d proofs", len(getResp.MapLeafInclusion), proofs)
   175  
   176  	glog.Info("Storing updated map values for domains...")
   177  	// Store updated map values:
   178  	setReq := &trillian.SetMapLeavesRequest{
   179  		MapId:  m.mapID,
   180  		Leaves: make([]*trillian.MapLeaf, 0, len(domains)),
   181  	}
   182  	for k, v := range domains {
   183  		index := ctmapper.HashDomain(k)
   184  		b, err := pb.Marshal(&v)
   185  		if err != nil {
   186  			return false, err
   187  		}
   188  		setReq.Leaves = append(setReq.Leaves, &trillian.MapLeaf{
   189  			Index:     index,
   190  			LeafValue: b,
   191  		})
   192  	}
   193  
   194  	mapperBytes, err := proto.Marshal(mapperMetadata)
   195  	if err != nil {
   196  		return false, fmt.Errorf("failed to marshal mapper metadata as 'bytes': err %v", err)
   197  	}
   198  
   199  	setReq.Metadata = mapperBytes
   200  
   201  	setResp, err := m.vmap.SetLeaves(context.Background(), setReq)
   202  	if err != nil {
   203  		return false, err
   204  	}
   205  	glog.Infof("Set resp: %v", setResp)
   206  	d := time.Since(start)
   207  	glog.Infof("Map run complete, took %.1f secs to update %d values (%0.2f/s)", d.Seconds(), len(setReq.Leaves), float64(len(setReq.Leaves))/d.Seconds())
   208  	return true, nil
   209  }
   210  
   211  func main() {
   212  	flag.Parse()
   213  	conn, err := grpc.Dial(*mapServer, grpc.WithInsecure())
   214  	if err != nil {
   215  		glog.Fatal(err)
   216  	}
   217  	defer conn.Close()
   218  
   219  	ctClient, err := client.New(*sourceLog, nil, jsonclient.Options{})
   220  	if err != nil {
   221  		glog.Exitf("Failed to create CT client: %v", err)
   222  	}
   223  	mapper := CTMapper{
   224  		mapID: int64(*mapID),
   225  		ct:    ctClient,
   226  		vmap:  trillian.NewTrillianMapClient(conn),
   227  	}
   228  	ctx := context.Background()
   229  
   230  	for {
   231  		moreToDo, err := mapper.oneMapperRun(ctx)
   232  		if err != nil {
   233  			glog.Warningf("mapper run failed: %v", err)
   234  		}
   235  		if !moreToDo {
   236  			time.Sleep(5 * time.Second)
   237  		}
   238  
   239  	}
   240  }