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 }