func MakeTPMPrin(verifier *rsa.PublicKey, pcrNums []int, pcrVals [][]byte) (auth.Prin, error) { aik, err := x509.MarshalPKIXPublicKey(verifier) if err != nil { return auth.Prin{}, err } name := auth.Prin{ Type: "tpm", Key: auth.Bytes(aik), } asp := auth.PrinExt{ Name: "PCRs", Arg: make([]auth.Term, 2), } var pcrNumStrs []string for _, v := range pcrNums { pcrNumStrs = append(pcrNumStrs, strconv.Itoa(v)) } asp.Arg[0] = auth.Str(strings.Join(pcrNumStrs, ",")) var pcrValStrs []string for _, p := range pcrVals { pcrValStrs = append(pcrValStrs, hex.EncodeToString(p)) } asp.Arg[1] = auth.Str(strings.Join(pcrValStrs, ",")) // The PCRs are the first extension of the name. name.Ext = []auth.PrinExt{asp} return name, nil }
// Search implements the subprinPrim custom datalog primitive by parsing // constant arguments of subprin/3 as principals and reporting any clauses it // discovers. func (sp *subprinPrim) Search(target *datalog.Literal, discovered func(c *datalog.Clause)) { p := target.Arg[0] o := target.Arg[1] e := target.Arg[2] if p.Constant() && o.Variable() && e.Variable() { prin, err := parseCompositePrin(p) if err != nil { return } extIndex := len(prin.Ext) - 1 trimmedPrin := auth.Prin{ Type: prin.Type, Key: prin.Key, Ext: prin.Ext[:extIndex], } extPrin := auth.PrinTail{ Ext: []auth.PrinExt{prin.Ext[extIndex]}, } parentIdent := dlengine.NewIdent(fmt.Sprintf("%q", trimmedPrin.String())) extIdent := dlengine.NewIdent(fmt.Sprintf("%q", extPrin.String())) discovered(datalog.NewClause(datalog.NewLiteral(sp, p, parentIdent, extIdent))) } else if p.Variable() && o.Constant() && e.Constant() { oprin, eprin, err := parseRootExtPrins(o, e) if err != nil { return } oprin.Ext = append(oprin.Ext, eprin.Ext...) oeIdent := dlengine.NewIdent(fmt.Sprintf("%q", oprin.String())) if len(oprin.Ext)+1 <= sp.max { discovered(datalog.NewClause(datalog.NewLiteral(sp, oeIdent, o, e))) } } else if p.Constant() && o.Constant() && e.Constant() { // Check that the constraint holds and report it as discovered. prin, err := parseCompositePrin(p) if err != nil { return } oprin, eprin, err := parseRootExtPrins(o, e) if err != nil { return } // Extend the root principal with the extension from the ext principal // and check identity. Make sure the constructed principal does // not exceed the given maximum principal length. oprin.Ext = append(oprin.Ext, eprin.Ext...) if prin.Identical(oprin) { discovered(datalog.NewClause(datalog.NewLiteral(sp, p, o, e))) } } }
func filenames(p *auth.Prin) []string { hash := fmt.Sprintf("%02x", sha256.Sum256([]byte(p.String()))) dir := path.Join(Directory, hash) fi, err := ioutil.ReadDir(dir) if err != nil { return nil } var names []string for _, f := range fi { if f.IsDir() { continue } names = append(names, path.Join(dir, f.Name())) } return names }
// This function generates a Program Certificate. In particular, it generates an attestation // signed by the domain policy key, with a statement of the form // 'policyKey says programCert speaksFor program' // where programCert is a X509 cert signed by the policy key with subject CommonName being the // Tao name of the program and subject public key being programKey. // Certificate expiration time is one year from issuing time. func GenerateProgramCert(domain *tao.Domain, serialNumber int, programPrin *auth.Prin, verifier *tao.Verifier, now, expiry time.Time) (*x509.Certificate, error) { policyCert := domain.Keys.Cert x509Info := domain.Config.GetX509Info() programName := programPrin.String() x509Info.CommonName = &programName x509Info.OrganizationalUnit = &programName subjectName := tao.NewX509Name(x509Info) clientCert, err := domain.Keys.SigningKey.CreateSignedX509( policyCert, serialNumber, verifier, subjectName) if err != nil { return nil, err } return clientCert, nil }
// TruncateAttestation cuts off a delegation chain at its "Program" subprincipal // extension and replaces its prefix with the given key principal. It also // returns the PrinExt that represents exactly the program hash. func TruncateAttestation(kprin auth.Prin, a *Attestation) (auth.Says, auth.PrinExt, error) { // This attestation must have a top-level delegation to a key. Return an // authorization for this program rooted in the policy key. I don't like // this, since it seems like it's much riskier, since this doesn't say // anything about the context in which the program is running. Fortunately, // local policy rules: if a peer won't accept this cert, then the other // program will have to fall back on the longer attestation. stmt, err := auth.UnmarshalForm(a.SerializedStatement) if err != nil { return auth.Says{}, auth.PrinExt{}, err } says, ok := stmt.(auth.Says) if !ok { return auth.Says{}, auth.PrinExt{}, fmt.Errorf("the serialized statement must be a says") } // Replace the message with one that uses the new principal, taking the last // Program subprinicpal, and all its following elements. It should say: // policyKey.Program(...)... says key(...) speaksfor // policyKey.Program(...)..., signed policyKey. sf, ok := says.Message.(auth.Speaksfor) if !ok { return auth.Says{}, auth.PrinExt{}, fmt.Errorf("the message in the statement must be a speaksfor") } delegator, ok := sf.Delegator.(auth.Prin) if !ok { return auth.Says{}, auth.PrinExt{}, fmt.Errorf("the delegator must be a principal") } var prog auth.PrinExt found := false for _, sprin := range delegator.Ext { if !found && (sprin.Name == "Program") { found = true prog = sprin } if found { kprin.Ext = append(kprin.Ext, sprin) } } // TODO(tmroeder): make sure that the delegate is a key and is not, e.g., // the policy key. truncSpeaksfor := auth.Speaksfor{ Delegate: sf.Delegate, Delegator: kprin, } truncSays := auth.Says{ Speaker: kprin, Time: says.Time, Expiration: says.Expiration, Message: truncSpeaksfor, } return truncSays, prog, nil }
func (m *idMap) add(prin auth.Prin) string { // Pick ids for the base principal name, i.e. the tao host // key(...) --> keyi // tpm(...) --> tpmi // Pick ids for all the subprincipal names // Prog(...) --> Programi // etc. p := prin.String() if s, ok := m.ids[p]; ok { return s } ext := prin.Ext prin.Ext = nil s := m.pick(template.HTMLEscapeString(prin.Type), prin.String()) for _, e := range ext { s += "." + m.pick(e.Name, e.String()) } tag := `<span class="id">[ %s ]<span class="pop"><span class="prin">%s</span></span></span>` m.ids[p] = fmt.Sprintf(tag, s, template.HTMLEscapeString(p)) return m.ids[p] }
func (m Manifest) Extend(p *auth.Prin, stmt auth.Form) { switch stmt := stmt.(type) { case auth.And: for _, f := range stmt.Conjunct { m.Extend(p, f) } case auth.Pred: if stmt.Name != "Manifest" || len(stmt.Arg) < 3 { return } if !p.Identical(stmt.Arg[0]) { return } s, ok := stmt.Arg[1].(auth.Str) if !ok { glog.Errorf("Ignoring manifest key of non-string type %T\n", stmt.Arg[1]) return } for i := 2; i < len(stmt.Arg)-1; i++ { if m[string(s)] == nil { m2 := Manifest{} m, m[string(s)] = m2, m2 } else { m2, ok := m[string(s)].(Manifest) if !ok { // Name clash with existing key, ignore new data. return } m = m2 } s, ok = stmt.Arg[i].(auth.Str) if !ok { glog.Errorf("Ignoring manifest key of non-string type %T\n", stmt.Arg[i]) return } } m[string(s)] = stmt.Arg[len(stmt.Arg)-1] } }
func main() { verbose.Set(true) options.Parse() profiling.ProfilePath = *options.String["profile"] if !verbose.Enabled { taoca.ConfirmNames = false } if *options.String["config"] != "" && !*options.Bool["init"] { err := options.Load(*options.String["config"]) options.FailIf(err, "Can't load configuration") } fmt.Println("https/tls Certificate Authority") manualMode = *options.Bool["manual"] learnMode = *options.Bool["learn"] if !manualMode && tao.Parent() == nil { options.Fail(nil, "can't continue: automatic mode, but no host Tao available") } if *options.Bool["root"] == (*options.String["subsidiary"] != "") { options.Usage("must supply exactly one of -root or -subsidiary options") } host := *options.String["host"] port := *options.String["port"] addr := net.JoinHostPort(host, port) // TODO(kwalsh) extend tao name with operating mode and policy cpath := *options.String["config"] kdir := *options.String["keys"] if kdir == "" && cpath != "" { kdir = path.Dir(cpath) } else if kdir == "" { options.Fail(nil, "Option -keys or -config is required") } ppath := path.Join(kdir, "policy") var err error if *options.Bool["init"] { if cpath != "" { err := options.Save(cpath, "HTTPS/TLS certificate authority configuration", "persistent") options.FailIf(err, "Can't save configuration") } fmt.Println("" + "Initializing fresh HTTP/TLS CA signing key. Provide the following information,\n" + "to be include in the CA's own x509 certificate. Leave the response blank to\n" + "accept the default value.\n" + "\n" + "Configuration file: " + cpath + "\n" + "Keys directory: " + kdir + "\n") var caName *pkix.Name if taoca.ConfirmNames { if *options.Bool["root"] { caName = taoca.ConfirmName(caRootName) } else { caName = taoca.ConfirmName(caSubsidiaryName) } } else { if *options.Bool["root"] { caName = caRootName } else { caName = caSubsidiaryName } } if manualMode { pwd := options.Password("Choose an HTTPS/TLS CA signing key password", "pass") caKeys, err = tao.InitOnDiskPBEKeys(tao.Signing, pwd, kdir, caName) tao.ZeroBytes(pwd) } else { caKeys, err = tao.InitOnDiskTaoSealedKeys(tao.Signing, caName, tao.Parent(), kdir, tao.SealPolicyDefault) } options.FailIf(err, "Can't initialize fresh HTTPS/TLS CA signing key") if *options.Bool["root"] { fmt.Printf(""+ "Note: To install this CA's key in the Chrome browser, go to\n"+ " 'Settings', 'Show advanced settings...', 'Manage Certificates...', 'Authorities'\n"+ " then import the following file:\n"+ " %s\n"+ " Select 'Trust this certificate for identifying websites' and/or other\n"+ " options, then click 'OK'\n", caKeys.X509Path("default")) } else { csr := taoca.NewCertificateSigningRequest(caKeys.VerifyingKey, caName) *csr.IsCa = true srv := *options.String["subsidiary"] taoca.DefaultServerName = srv taoca.SubmitAndInstall(caKeys, csr) } if !manualMode { f, err := os.Open(ppath) if err == nil { f.Close() fmt.Printf("Using existing certificate-granting policy: %s\n", ppath) } else { fmt.Printf("Creating default certificate-granting policy: %s\n", ppath) fmt.Printf("Edit that file to define the certificate-granting policy.\n") err := util.WritePath(ppath, []byte(policy.Default), 0755, 0755) options.FailIf(err, "Can't save policy rules") } } } else { if manualMode { pwd := options.Password("HTTPS/TLS CA signing key password", "pass") caKeys, err = tao.LoadOnDiskPBEKeys(tao.Signing, pwd, kdir) tao.ZeroBytes(pwd) } else { caKeys, err = tao.LoadOnDiskTaoSealedKeys(tao.Signing, tao.Parent(), kdir, tao.SealPolicyDefault) } options.FailIf(err, "Can't load HTTP/TLS CA signing key") } netlog.Log("https_ca: start") netlog.Log("https_ca: manual? %v", manualMode) if !manualMode { guard, err = policy.Load(ppath) options.FailIf(err, "Can't load certificate-granting policy") } var prin auth.Prin if tao.Parent() != nil { prin, err = tao.Parent().GetTaoName() options.FailIf(err, "Can't get tao name") } else { rendezvous.DefaultServer.Connect(caKeys) prin = caKeys.SigningKey.ToPrincipal() } name := *options.String["name"] if name != "" { err = rendezvous.Register(rendezvous.Binding{ Name: proto.String(name), Host: proto.String(host), Port: proto.String(port), Protocol: proto.String("protoc/rpc/https_ca"), Principal: proto.String(prin.String()), }) options.FailIf(err, "Can't register with rendezvous service") } statsdelay := *options.String["stats"] var srv *tao.Server if statsdelay != "" { go profiling.ShowStats(&stats, statsdelay, "sign certificates") srv = tao.NewOpenServer(tao.ConnHandlerFunc(doResponseWithStats)) } else { srv = tao.NewOpenServer(tao.ConnHandlerFunc(doResponseWithoutStats)) } srv.Keys = caKeys fmt.Printf("Listening at %s using Tao-authenticated channels\n", addr) err = srv.ListenAndServe(addr) options.FailIf(err, "server died") fmt.Println("Server Done") netlog.Log("https_ca: done") }
// Derive returns a manifest for a principal based on previously published info // and/or the principal name itself. func DeriveManifest(p *auth.Prin) Manifest { m := Manifest{} parent := p.Parent() if parent == nil { if p.Type == "tpm" { pcr := Manifest{} nums, vals, err := ExtractPCRs(*p) if err != nil { pcr["Status"] = "Unknown" } else { for i := 0; i < len(nums) && i < len(vals); i++ { pcr[fmt.Sprintf("PCR %d", nums[i])] = vals[i] } } aik := Manifest{} k, err := ExtractAIK(*p) if err != nil { aik["Status"] = "Unknown" } else { aik["Type"] = "RSA" aik["Size"] = k.N.BitLen() aik["Exponent"] = k.E aik["Modulus"] = k.N.Bytes() } m["Type"] = "Trusted Platform Module" m["TPM"] = Manifest{ "Platform Configuration Registers": pcr, "Public Attestation Identity Key": aik, } } else if p.Type == "key" { key := Manifest{} v, err := FromPrincipal(*p) if err != nil { key["Status"] = "Unknown" } else { switch v := v.PublicKey().(type) { case *ecdsa.PublicKey: key["Algorithm"] = "ECDSA" key["Curve"] = ecdsaCurveName[v.Curve] key["X"] = v.X.Bytes() key["Y"] = v.Y.Bytes() } } m["Type"] = "Public Key Principal" m["Key"] = key } else { m["Type"] = "Unrecognized" } return m } else { m["Subprincipal Extension"] = p.Ext.String() } for _, f := range filenames(p) { b, err := ioutil.ReadFile(f) // TODO(kwalsh) reap expired and malformed files if err != nil { continue } var a Attestation if err = proto.Unmarshal(b, &a); err != nil { glog.Errorf("Ignoring malformed manifest %s\n", f) continue } says, err := a.Validate() if err != nil { glog.Errorf("Ignoring invalid manifest %s\n", f) continue } if !says.Speaker.Identical(parent) { glog.Errorf("Ignoring misplaced manifest %s\n", f) continue } if !says.Active(time.Now().UnixNano()) { glog.Errorf("Ignoring expired manifest %s\n", f) continue } m.Extend(p, says.Message) } m["Parent"] = DeriveManifest(parent) return m }