// RunMessageLoop handles messages from a client on a given message stream until // it gets an error trying to read a message. func (m *ResourceMaster) RunMessageLoop(ms util.MessageStream, programPolicy *ProgramPolicy) error { for { var msg Message if err := ms.ReadMessage(&msg); err != nil { return err } var fop FileOperation t := *msg.Type if t == MessageType_CREATE || t == MessageType_DELETE || t == MessageType_READ || t == MessageType_WRITE { if err := proto.Unmarshal(msg.Data, &fop); err != nil { log.Printf("Couldn't unmarshal FileOperation for operation %d\n", t) continue } if err := m.checkFileAuth(&msg, &fop); err != nil { log.Printf("The file operation %d didn't pass authorization: %s\n", t, err) continue } } switch *msg.Type { case MessageType_AUTH_CERT: cert, err := m.AuthenticatePrincipal(ms, &msg, programPolicy) if err != nil { log.Printf("Failed to authenticate a principal: %s\n", err) continue } owner, err := principalNameFromDERCert(cert) if err != nil { log.Printf("Couldn't get the owner name from the cert: %s\n", err) continue } _, err = m.InsertPrincipal(owner, cert, Authenticated) if err != nil { log.Printf("Couldn't set the principal as authenticated") } case MessageType_CREATE: if err := m.Create(ms, &fop); err != nil { log.Printf("Couldn't create the file %s: %s\n", *fop.Name, err) } case MessageType_READ: if err := m.Read(ms, &fop, programPolicy.SymKeys); err != nil { log.Printf("Couldn't create the file %s: %s\n", *fop.Name, err) } case MessageType_WRITE: if err := m.Write(ms, &fop, programPolicy.SymKeys); err != nil { log.Printf("Couldn't create the file %s: %s\n", *fop.Name, err) } default: if err := sendResult(ms, false); err != nil { log.Printf("Couldn't signal failure for the invalid operation: %s", err) } log.Printf("Invalid initial message type %d\n", *msg.Type) } } }
// checkResponse waits for a RollbackResponse and checks to make sure it's not // an ERROR response from the server. func checkResponse(ms *util.MessageStream) error { var rr RollbackResponse if err := ms.ReadMessage(&rr); err != nil { return err } if *rr.Type == RollbackMessageType_ERROR { return fmt.Errorf("couldn't set the counter on the remote server") } return nil }
func sendError(err error, ms *util.MessageStream) { var errStr = "" if err != nil { errStr = err.Error() } resp := &domain_service.DomainServiceResponse{ErrorMessage: &errStr} if _, err := ms.WriteMessage(resp); err != nil { log.Printf("domain_server: Error sending resp on the channel: %s\n ", err) } }
func SendMessage(ms *util.MessageStream, msg *SimpleMessage) error { out, err := proto.Marshal(msg) if err != nil { return errors.New("SendRequest: Can't encode response") } send := string(out) _, err = ms.WriteString(send) if err != nil { return errors.New("SendResponse: Writestring error") } return nil }
// recvResult waits for a OperationResult on a MessageStream func recvResult(ms util.MessageStream) (bool, error) { var m Message if err := ms.ReadMessage(&m); err != nil { return false, err } var res OperationResult if err := proto.Unmarshal(m.Data, &res); err != nil { return false, err } return *res.Result, nil }
func GetMessage(ms *util.MessageStream) (*SimpleMessage, error) { resp, err := ms.ReadString() if err != nil { return nil, err } msg := new(SimpleMessage) err = proto.Unmarshal([]byte(resp), msg) if err != nil { return nil, errors.New("GetResponse: Can't unmarshal message") } return msg, nil }
// SetCounter sets the remote counter for this program. func SetCounter(ms *util.MessageStream, counter uint64) error { rm := &RollbackMessage{ Type: RollbackMessageType_SET_COUNTER.Enum(), Data: EncodeCounter(counter), } if _, err := ms.WriteMessage(rm); err != nil { return err } // TODO(tmroeder): we currently ignore the value of the counter returned // by the server. return checkResponse(ms) }
// readResult reads an OperationResult and returns its value or an error. func readResult(ms util.MessageStream) (bool, error) { // Read the response wrapper message. var arm Message if err := ms.ReadMessage(&arm); err != nil { return false, err } if *arm.Type != MessageType_OP_RES { return false, fmt.Errorf("didn't receive OP_RES from the server") } var opr OperationResult if err := proto.Unmarshal(arm.Data, &opr); err != nil { return false, err } return *opr.Result, nil }
// sendResult sends an OperationResult with the given value on the given stream. func sendResult(ms util.MessageStream, result bool) error { res := &OperationResult{Result: proto.Bool(result)} ar := &Message{ Type: MessageType_OP_RES.Enum(), } var err error if ar.Data, err = proto.Marshal(res); err != nil { return err } if _, err := ms.WriteMessage(ar); err != nil { return err } return nil }
func (conn *Conn) sendCredentials(ms util.MessageStream) { if conn.Authenticate && conn.delegation != nil { ms.WriteString("delegation") ms.WriteMessage(conn.delegation) } else if conn.Authenticate { ms.WriteString("key") } else { ms.WriteString("anonymous") } }
// RunMessageLoop handles incoming messages for the RollbackMaster and passes // them to the appropriate functions. func (r *RollbackMaster) RunMessageLoop(ms *util.MessageStream, programPolicy *ProgramPolicy, name string) error { for { var msg RollbackMessage if err := ms.ReadMessage(&msg); err != nil { return err } switch *msg.Type { case RollbackMessageType_SET_COUNTER: i, err := decodeCounter(msg.Data) if err != nil { log.Printf("failed to decode counter for SET_COUNTER: %s", err) continue } if err = r.SetCounter(ms, name, i); err != nil { log.Printf("failed to set the counter on the RollbackMaster: %s", err) continue } case RollbackMessageType_GET_COUNTER: if err := r.GetCounter(ms, name); err != nil { log.Printf("failed to get the counter for program %s", name) continue } case RollbackMessageType_SET_HASH: var rh RollbackHash if err := proto.Unmarshal(msg.Data, &rh); err != nil { log.Printf("failed to unmarshal the parameters for SET_HASH: %s", err) continue } if err := r.SetHash(ms, name, *rh.Item, rh.Hash); err != nil { log.Printf("failed to set the hash for item %s on program %s: %s", *rh.Item, name, err) continue } case RollbackMessageType_GET_HASHED_VERIFIER: if err := r.GetHashedVerifier(ms, name, string(msg.Data)); err != nil { log.Printf("failed to get the hashed verifier for program %s: %s", name, err) continue } default: log.Printf("unknown rollback message %d", *msg.Type) } } }
// SetHash sets the value of a hash for a given item for this program. func SetHash(ms *util.MessageStream, item string, hash []byte) error { rh := &RollbackHash{ Item: proto.String(item), Hash: hash, } rhb, err := proto.Marshal(rh) if err != nil { return err } rm := &RollbackMessage{ Type: RollbackMessageType_SET_HASH.Enum(), Data: rhb, } if _, err := ms.WriteMessage(rm); err != nil { return err } return checkResponse(ms) }
// GetCounter implements RollbackMessageType_GET_COUNTER and returns the current // value of a counter to the requestor. func (r *RollbackMaster) GetCounter(ms *util.MessageStream, name string) error { var emptyData []byte rr := &RollbackResponse{ Type: RollbackMessageType_ERROR.Enum(), Data: emptyData, } p := r.FindRollbackProgram(name) if p == nil { if _, err := ms.WriteMessage(rr); err != nil { return err } return fmt.Errorf("couldn't find a rollback program with name %s", name) } rr.Type = RollbackMessageType_GET_COUNTER.Enum() rr.Data = EncodeCounter(p.MonotonicCounter) _, err := ms.WriteMessage(rr) return err }
// GetHashedVerifier gets the hash of the counter and the item hash for a given // item. func GetHashedVerifier(ms *util.MessageStream, item string) ([]byte, error) { rm := &RollbackMessage{ Type: RollbackMessageType_GET_HASHED_VERIFIER.Enum(), Data: []byte(item), } if _, err := ms.WriteMessage(rm); err != nil { return nil, err } // We can't use checkResponse here since we need to get the value out of // the response to read the hash. var rr RollbackResponse if err := ms.ReadMessage(&rr); err != nil { return nil, err } if *rr.Type == RollbackMessageType_ERROR { return nil, fmt.Errorf("couldn't set the counter on the remote server") } return rr.Data, nil }
// sendOperation is a helper method that sets up the data structures needed for // a FileOperation message like CREATE, WRITE, or READ, and sends this message // on the MessageStream. func sendOperation(ms util.MessageStream, mt MessageType, cert []byte, name string) error { fop := &FileOperation{ Subject: cert, Name: proto.String(name), } fopb, err := proto.Marshal(fop) if err != nil { return err } m := &Message{ Type: mt.Enum(), Data: fopb, } if _, err := ms.WriteMessage(m); err != nil { return err } return nil }
// SetCounter sets the monotonic counter for a given program to a higher value. // It returns an error if the program doesn't exist or if the new value of the // counter is less than the current value of the counter. func (r *RollbackMaster) SetCounter(ms *util.MessageStream, name string, counter uint64) error { var emptyData []byte rr := &RollbackResponse{ Type: RollbackMessageType_ERROR.Enum(), Data: emptyData, } p := r.FindRollbackProgram(name) if p == nil { if _, err := ms.WriteMessage(rr); err != nil { return err } return fmt.Errorf("couldn't find a rollback program with name %s", name) } if p.MonotonicCounter > counter { if _, err := ms.WriteMessage(rr); err != nil { return err } return fmt.Errorf("couldn't write a smaller counter value %d for %s", counter, name) } // TODO(tmroeder): this needs synchronization for any real application. p.MonotonicCounter = counter rr.Type = RollbackMessageType_SET_COUNTER.Enum() rr.Data = EncodeCounter(p.MonotonicCounter) if _, err := ms.WriteMessage(rr); err != nil { return err } return nil }
// GetCounter gets the current value of the monotonic counter for a given // program name. func GetCounter(ms *util.MessageStream) (uint64, error) { // The name of the program is managed by the rollback server, not the // client, so it doesn't need to be passed in the message. rm := &RollbackMessage{ Type: RollbackMessageType_GET_COUNTER.Enum(), Data: make([]byte, 0), } if _, err := ms.WriteMessage(rm); err != nil { return 0, err } // We can't use checkResponse here since we need to get the value out of // the response to read the counter. var rr RollbackResponse if err := ms.ReadMessage(&rr); err != nil { return 0, err } if *rr.Type == RollbackMessageType_ERROR { return 0, fmt.Errorf("couldn't set the counter on the remote server") } return decodeCounter(rr.Data) }
// SetHash implements RollbackMessageType_SET_HASH by setting the value of the // hash for a given item to a given hash value. func (r *RollbackMaster) SetHash(ms *util.MessageStream, name string, item string, h []byte) error { var emptyData []byte rr := &RollbackResponse{ Type: RollbackMessageType_ERROR.Enum(), Data: emptyData, } p := r.FindRollbackProgram(name) if p == nil { if _, err := ms.WriteMessage(rr); err != nil { return err } return fmt.Errorf("couldn't find a rollback program with name %s", name) } // Set the hash. if err := p.AddRollbackHashEntry(item, h); err != nil { if _, e := ms.WriteMessage(rr); e != nil { return e } return err } rh := &RollbackHash{ Item: proto.String(item), Hash: h, } rhb, err := proto.Marshal(rh) if err != nil { if _, e := ms.WriteMessage(rr); e != nil { return e } return err } // TODO(tmroeder): Do you need to update the counter when you update the // hash? rr.Type = RollbackMessageType_SET_HASH.Enum() rr.Data = rhb _, err = ms.WriteMessage(rr) return err }
// AuthenticatePrincipal is a client method used to send a request to a // ResourceMaster to authenticate a principal with a given certificate and a // given set of keys. func AuthenticatePrincipal(ms util.MessageStream, key *tao.Keys, derCert []byte) error { // Send the authentication request, which supposes that a server is // waiting to receive this request. c := &Message{ Type: MessageType_AUTH_CERT.Enum(), Data: derCert, } if _, err := ms.WriteMessage(c); err != nil { return err } // Receive a challenge nonce from the server. var nc Message if err := ms.ReadMessage(&nc); err != nil { return err } if *nc.Type != MessageType_NONCE_CHALL { return fmt.Errorf("didn't receive NONCE_CHALL from the server") } // Sign the nonce. sn := &Message{ Type: MessageType_SIGNED_NONCE.Enum(), } var err error if sn.Data, err = key.SigningKey.Sign(nc.Data, ChallengeContext); err != nil { return err } if _, err := ms.WriteMessage(sn); err != nil { return err } // Get the result from the server after verificaton. res, err := readResult(ms) if err != nil { return err } if !res { return fmt.Errorf("the signed nonce failed verification") } return nil }
// GetHashedVerifier gets a version of the hash for a given item along with the // current monotonic counter. func (r *RollbackMaster) GetHashedVerifier(ms *util.MessageStream, name string, item string) error { var emptyData []byte rr := &RollbackResponse{ Type: RollbackMessageType_ERROR.Enum(), Data: emptyData, } p := r.FindRollbackProgram(name) if p == nil { if _, err := ms.WriteMessage(rr); err != nil { return err } return fmt.Errorf("couldn't find a rollback program with name %s", name) } h := p.FindRollbackHashEntry(item) if h == nil { if _, err := ms.WriteMessage(rr); err != nil { return err } return fmt.Errorf("couldn't find an item with name '%s' in program '%s'", item, name) } // Return SHA-256(Counter || Hash || Counter). // TODO(tmroeder): what is the justification for this protocol? sha256Hash := sha256.New() b := EncodeCounter(p.MonotonicCounter) sha256Hash.Write(b) sha256Hash.Write(h) sha256Hash.Write(b) hash := sha256Hash.Sum(nil) rr.Type = RollbackMessageType_GET_HASHED_VERIFIER.Enum() rr.Data = hash[:] _, err := ms.WriteMessage(rr) return err }
// SendFile reads a file from disk and streams it to a receiver across a // MessageStream. If there are sufficient bytes in the keys (at least // hmacKeySize+aesKeySize), then it will attempt to check the integrity of the // file with HMAC-SHA256 and decrypt it with AES-CTR-128. func SendFile(ms util.MessageStream, dir string, filename string, keys []byte) error { fullpath := path.Join(dir, filename) fileInfo, err := os.Stat(fullpath) if err != nil { return fmt.Errorf("in SendFile: no file '%s' found: %s", fullpath, err) } file, err := os.Open(fullpath) if err != nil { return fmt.Errorf("in SendFile: can't open file '%s': %s", fullpath, err) } defer file.Close() // This encryption scheme uses AES-CTR with HMAC-SHA256 for integrity // protection. var hm hash.Hash var ctr cipher.Stream iv := make([]byte, ivSize) hasKeys := len(keys) >= minKeySize // The variable "left" gives the total number of bytes left to read from // the (maybe encrypted) file. left := fileInfo.Size() buf := make([]byte, bufferSize) if hasKeys { dec, err := aes.NewCipher(keys[:aesKeySize]) if err != nil || dec == nil { return fmt.Errorf("can't create AES cipher in SendFile: %s", err) } if _, err := file.Read(iv); err != nil { return err } // Remove the IV from the number of remaining bytes to decrypt. left = left - ivSize // Take all the remaining key bytes for the HMAC key. hm = hmac.New(sha256.New, keys[aesKeySize:]) hmacSize := hm.Size() // The HMAC input starts with the IV. hm.Write(iv) ctr = cipher.NewCTR(dec, iv) if ctr == nil { return fmt.Errorf("can't create AES-CTR encryption") } // Remove the HMAC-SHA256 output from the bytes to check. left = left - int64(hmacSize) // Secure decryption in this case requires reading the file // twice: once to check the MAC, and once to decrypt the bytes. // The MAC must be checked before *any* decryption occurs and // before *any* decrypted bytes are sent to the receiver. for { // Figure out how many bytes to read on this iteration. readSize := int64(bufferSize) final := false if left <= bufferSize { readSize = left final = true } // Read the (maybe encrypted) bytes from the file. n, err := file.Read(buf[:readSize]) if err != nil { return err } left = left - int64(n) hm.Write(buf[:n]) if final { break } } computed := hm.Sum(nil) original := buf[:hmacSize] // Read the file's version of the HMAC and check it securely // against the computed version. if _, err := file.Read(original); err != nil { return err } if !hmac.Equal(computed, original) { return fmt.Errorf("invalid file HMAC on decryption for file '%s'", fullpath) } // Go back to the beginning of the file (minus the IV) for // decryption. if _, err := file.Seek(ivSize, 0); err != nil { return fmt.Errorf("couldn't seek back to the beginning of file '%s': %s", fullpath, err) } // Reset the number of bytes so it only includes the encrypted // bytes. left = fileInfo.Size() - int64(ivSize+hmacSize) } // The input buffer, and a temporary buffer for holding decrypted // plaintext. temp := make([]byte, bufferSize) // Set up a framing message to use to send the data. m := &Message{ Type: MessageType_FILE_NEXT.Enum(), } // Now that the integrity of the data has been verified, if needed, send // the data (after decryption, if necessary) to the receiver. for { // Figure out how many bytes to read on this iteration. readSize := int64(bufferSize) final := false if left <= bufferSize { readSize = left final = true m.Type = MessageType_FILE_LAST.Enum() } // Read the (maybe encrypted) bytes from the file. n, err := file.Read(buf[:readSize]) if err != nil { return err } left = left - int64(n) if hasKeys { ctr.XORKeyStream(temp[:n], buf[:n]) m.Data = temp[:n] } else { m.Data = buf[:n] } // Send the decrypted data to the receiver. if _, err := ms.WriteMessage(m); err != nil { return err } if final { break } } return nil }
// GetFile receives bytes from a sender and optionally encrypts them and adds // integrity protection, and writes them to disk. func GetFile(ms util.MessageStream, dir string, filename string, keys []byte) error { fullpath := path.Join(dir, filename) file, err := os.Create(fullpath) if err != nil { return fmt.Errorf("can't create file '%s' in GetFile", fullpath) } defer file.Close() var ctr cipher.Stream var hm hash.Hash iv := make([]byte, ivSize) hasKeys := len(keys) >= minKeySize if hasKeys { enc, err := aes.NewCipher(keys[:aesKeySize]) if err != nil || enc == nil { return fmt.Errorf("couldn't create an AES cipher: %s", err) } // Use the remaining bytes of the key slice for the HMAC key. hm = hmac.New(sha256.New, keys[aesKeySize:]) if _, err := rand.Read(iv); err != nil { return fmt.Errorf("couldn't read random bytes for a fresh IV: %s", err) } // The first bytes of the HMAC input are the IV. hm.Write(iv) ctr = cipher.NewCTR(enc, iv) if ctr == nil { return fmt.Errorf("couldn't create a new instance of AES-CTR-128") } if _, err = file.Write(iv); err != nil { return err } } // temp holds temporary encrypted ciphertext before it's written to // disk. temp := make([]byte, bufferSize) for { var m Message if err := ms.ReadMessage(&m); err != nil { return nil } // Sanity check: this must be FILE_LAST or FILE_NEXT. t := *m.Type if !(t == MessageType_FILE_LAST || t == MessageType_FILE_NEXT) { return fmt.Errorf("received invalid message type %d during file streaming in GetFile", t) } if hasKeys { l := len(m.Data) ctr.XORKeyStream(temp, m.Data) hm.Write(temp[:l]) if _, err = file.Write(temp[:l]); err != nil { return err } } else { if _, err = file.Write(m.Data); err != nil { return err } } // FILE_LAST corresponds to receiving the final bytes of the // file. if *m.Type == MessageType_FILE_LAST { break } } // Write the MAC at the end of the file. if hasKeys { hmacBytes := hm.Sum(nil) if _, err = file.Write(hmacBytes[:]); err != nil { return err } } return nil }
func (conn *Conn) recvCredentials(ms util.MessageStream) { m, err := ms.ReadString() if err != nil { return } if m == "delegation" { var a Attestation if err = ms.ReadMessage(&a); err != nil { return } // Validate the peer certificate peerCert := conn.ConnectionState().PeerCertificates[0] p, err := ValidatePeerAttestation(&a, peerCert) if err != nil { ms.SetErr(err) return } if conn.guard != nil { if conn.verifier != nil { if err = AddEndorsements(conn.guard, &a, conn.verifier); err != nil { ms.SetErr(err) return } } if !conn.guard.IsAuthorized(p, "Connect", nil) { ms.SetErr(errors.New("principal delegator in client attestation is not authorized to connect")) return } } conn.peer = &p } else if m == "key" { peerCert := conn.ConnectionState().PeerCertificates[0] v, err := FromX509(peerCert) if err != nil { ms.SetErr(errors.New("can't decode key from peer certificate")) return } p := v.ToPrincipal() conn.peer = &p } else if m == "anonymous" { if conn.guard != nil { err = errors.New("peer did not provide tao delegation") ms.SetErr(err) return } } else { err = errors.New("unrecognized authentication handshake: " + m) ms.SetErr(err) return } }
func sendResponse(ms util.MessageStream, resp *taoca.Response) { _, err := ms.WriteMessage(resp) if err != nil { fmt.Printf("error writing response: %s\n", err) } }
// AuthenticatePrincipal runs a synchronous protocol to authenticate a single // principal on a single channel. In this toy implementation, it is assumed that // there are no other principals on the channel and that there are no other // simultaneous channels. func (m *ResourceMaster) AuthenticatePrincipal(ms util.MessageStream, msg *Message, programPolicy *ProgramPolicy) ([]byte, error) { // The certificate message is passed in by the caller as the first // message. // Process the certificate. For AUTH_CERT, the data is just the // certificate. cert, err := x509.ParseCertificate([]byte(msg.Data)) if err != nil { log.Printf("couldn't Parse Certificate in AuthenticatePrincipal\n") return nil, err } // Set up a nonce challenge for the reply. For NONCE_CHALL, the data is // also just the message itself. reply := &Message{ Type: MessageType_NONCE_CHALL.Enum(), Data: make([]byte, NonceSize), } if _, err = rand.Read(reply.Data); err != nil { return nil, err } // Step 1: Send a nonce to the principal. if _, err := ms.WriteMessage(reply); err != nil { return nil, err } // Step 2: Wait for the signed response. var s Message if err := ms.ReadMessage(&s); err != nil { return nil, err } if *s.Type != MessageType_SIGNED_NONCE { return nil, fmt.Errorf("received message was not SIGNED_NONCE") } // Verify the certificate against the root. // TODO(tmroeder): move the VerifyOptions up into the ResourceMaster. var opts x509.VerifyOptions roots := x509.NewCertPool() policyCert, err := x509.ParseCertificate(programPolicy.PolicyCert) if err != nil || policyCert == nil { return nil, err } roots.AddCert(policyCert) opts.Roots = roots chains, err := cert.Verify(opts) if chains == nil || err != nil { return nil, err } v, err := tao.FromX509(cert) if err != nil { return nil, err } ok, err := v.Verify(reply.Data, ChallengeContext, s.Data) if err != nil { return nil, err } if err := sendResult(ms, ok); err != nil { return nil, fmt.Errorf("failed to return a result to the client") } if !ok { return nil, fmt.Errorf("the nonce signature did not pass verification") } return msg.Data, nil }