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 (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") } }
// 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) }
// 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 }
// 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) }
// 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 }
// 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 }
// 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 }
// 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 }
func sendResponse(ms util.MessageStream, resp *taoca.Response) { _, err := ms.WriteMessage(resp) if err != nil { fmt.Printf("error writing response: %s\n", 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 }