github.com/jlmucb/cloudproxy@v0.0.0-20170830161738-b5aa0b619bc4/go/tao/rpc.go (about) 1 // Copyright (c) 2014, Kevin Walsh. 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 package tao 16 17 // This provides client stubs for the Tao interface. This code is (mostly) 18 // extremely dull and, ideally, would be generated automatically. 19 20 import ( 21 "errors" 22 "io" 23 "math" 24 "net/rpc" 25 "strings" 26 27 "github.com/golang/protobuf/proto" 28 "github.com/jlmucb/cloudproxy/go/tao/auth" 29 "github.com/jlmucb/cloudproxy/go/util" 30 "github.com/jlmucb/cloudproxy/go/util/protorpc" 31 ) 32 33 // RPC sends requests between this hosted program and the host Tao. 34 type RPC struct { 35 rpc *rpc.Client 36 serviceName string 37 } 38 39 // DeserializeRPC produces a RPC from a string. 40 func DeserializeRPC(s string) (*RPC, error) { 41 if s == "" { 42 return nil, newError("taorpc: missing host Tao spec" + 43 " (ensure $" + HostSpecEnvVar + " is set)") 44 } 45 r := strings.TrimPrefix(s, "tao::RPC+") 46 if r == s { 47 return nil, newError("taorpc: unrecognized $" + HostSpecEnvVar + " string " + s) 48 } 49 ms, err := util.DeserializeFDMessageStream(r) 50 if err != nil { 51 return nil, newError("taorpc: unrecognized $" + HostSpecEnvVar + " string " + s + 52 " (" + err.Error() + ")") 53 } 54 return &RPC{protorpc.NewClient(ms), "Tao"}, nil 55 } 56 57 // DeserializeFileRPC produces a RPC from a string representing a file. 58 func DeserializeFileRPC(s string) (*RPC, error) { 59 if s == "" { 60 return nil, newError("taorpc: missing host Tao spec" + 61 " (ensure $" + HostSpecEnvVar + " is set)") 62 } 63 r := strings.TrimPrefix(s, "tao::RPC+") 64 if r == s { 65 return nil, newError("taorpc: unrecognized $" + HostSpecEnvVar + " string " + s) 66 } 67 ms, err := util.DeserializeFileMessageStream(r) 68 if err != nil { 69 return nil, newError("taorpc: unrecognized $" + HostSpecEnvVar + " string " + s + 70 " (" + err.Error() + ")") 71 } 72 return &RPC{protorpc.NewClient(ms), "Tao"}, nil 73 } 74 75 // DeserializeUnixSocketRPC produces a RPC from a path string. 76 func DeserializeUnixSocketRPC(p string) (*RPC, error) { 77 if p == "" { 78 return nil, newError("taorpc: missing host Tao spec" + 79 " (ensure $" + HostSpecEnvVar + " is set)") 80 } 81 82 ms, err := util.DeserializeUnixSocketMessageStream(p) 83 if err != nil { 84 return nil, err 85 } 86 87 return &RPC{protorpc.NewClient(ms), "Tao"}, nil 88 } 89 90 // NewRPC constructs a RPC for the default gob encoding rpc client using 91 // an io.ReadWriteCloser. 92 func NewRPC(rwc io.ReadWriteCloser, serviceName string) (*RPC, error) { 93 return &RPC{rpc.NewClient(rwc), serviceName}, nil 94 } 95 96 type expectedResponse int 97 98 const ( 99 wantNothing expectedResponse = 0 100 wantData expectedResponse = 1 << iota 101 wantPolicy 102 wantLabel 103 wantCounter 104 ) 105 106 // An ErrMalformedResponse is returned as an error for an invalid response. 107 var ErrMalformedResponse = errors.New("taorpc: malformed response") 108 109 // call issues an rpc request, obtains the response, checks the response for 110 // errors, and checks that the response contains exactly the expected values. 111 func (t *RPC) call(method string, r *RPCRequest, e expectedResponse) (data []byte, policy string, 112 counter int64, err error) { 113 s := new(RPCResponse) 114 err = t.rpc.Call(method, r, s) 115 if err != nil { 116 return 117 } 118 if (s.Data != nil) != (e&wantData != 0) || 119 (s.Policy != nil) != (e&wantPolicy != 0) { 120 err = ErrMalformedResponse 121 return 122 } 123 if s.Data != nil { 124 data = s.Data 125 } 126 if s.Policy != nil { 127 policy = *s.Policy 128 } 129 if s.Counter != nil { 130 counter = *s.Counter 131 } 132 return 133 } 134 135 // GetTaoName implements part of the Tao interface. 136 func (t *RPC) GetTaoName() (auth.Prin, error) { 137 r := &RPCRequest{} 138 data, _, _, err := t.call(t.serviceName+".GetTaoName", r, wantData) 139 if err != nil { 140 return auth.Prin{}, err 141 } 142 return auth.UnmarshalPrin(data) 143 } 144 145 // ExtendTaoName implements part of the Tao interface. 146 func (t *RPC) ExtendTaoName(subprin auth.SubPrin) error { 147 r := &RPCRequest{Data: auth.Marshal(subprin)} 148 _, _, _, err := t.call(t.serviceName+".ExtendTaoName", r, wantNothing) 149 return err 150 } 151 152 type taoRandReader RPC 153 154 // Read implements part of the Tao interface. 155 func (t *taoRandReader) Read(p []byte) (n int, err error) { 156 bytes, err := (*RPC)(t).GetRandomBytes(len(p)) 157 if err != nil { 158 return 0, err 159 } 160 copy(p, bytes) 161 return len(p), nil 162 } 163 164 // TODO(kwalsh) Can Rand be made generic, or does it need to be defined for the 165 // concrete type RPC? 166 167 // Rand implements part of the Tao interface. 168 func (t *RPC) Rand() io.Reader { 169 return (*taoRandReader)(t) 170 } 171 172 // GetRandomBytes implements part of the Tao interface. 173 func (t *RPC) GetRandomBytes(n int) ([]byte, error) { 174 if n > math.MaxUint32 { 175 return nil, newError("taorpc: request for too many random bytes") 176 } 177 r := &RPCRequest{Size: proto.Int32(int32(n))} 178 bytes, _, _, err := t.call(t.serviceName+".GetRandomBytes", r, wantData) 179 return bytes, err 180 } 181 182 // GetSharedSecret implements part of the Tao interface. 183 func (t *RPC) GetSharedSecret(n int, policy string) ([]byte, error) { 184 if n > math.MaxUint32 { 185 return nil, newError("taorpc: request for too many secret bytes") 186 } 187 r := &RPCRequest{Size: proto.Int32(int32(n)), Policy: proto.String(policy)} 188 bytes, _, _, err := t.call(t.serviceName+".GetSharedSecret", r, wantData) 189 return bytes, err 190 } 191 192 // Attest implements part of the Tao interface. 193 func (t *RPC) Attest(issuer *auth.Prin, time, expiration *int64, message auth.Form) (*Attestation, error) { 194 var issuerBytes []byte 195 if issuer != nil { 196 issuerBytes = auth.Marshal(*issuer) 197 } 198 r := &RPCRequest{ 199 Issuer: issuerBytes, 200 Time: time, 201 Expiration: expiration, 202 Data: auth.Marshal(message), 203 } 204 bytes, _, _, err := t.call(t.serviceName+".Attest", r, wantData) 205 if err != nil { 206 return nil, err 207 } 208 var a Attestation 209 err = proto.Unmarshal(bytes, &a) 210 if err != nil { 211 return nil, err 212 } 213 return &a, nil 214 } 215 216 // Seal implements part of the Tao interface. 217 func (t *RPC) Seal(data []byte, policy string) (sealed []byte, err error) { 218 r := &RPCRequest{Data: data, Policy: proto.String(policy)} 219 sealed, _, _, err = t.call(t.serviceName+".Seal", r, wantData) 220 return 221 } 222 223 // Unseal implements part of the Tao interface. 224 func (t *RPC) Unseal(sealed []byte) (data []byte, policy string, err error) { 225 r := &RPCRequest{Data: sealed} 226 data, policy, _, err = t.call(t.serviceName+".Unseal", r, wantData|wantPolicy) 227 return 228 } 229 230 func (t *RPC) InitCounter(label string, c int64) (err error) { 231 r := &RPCRequest{Label: &label, Counter: &c} 232 _, _, _, err = t.call(t.serviceName+".InitCounter", r, wantNothing) 233 return 234 } 235 236 func (t *RPC) GetCounter(label string) (c int64, err error) { 237 r := &RPCRequest{Label: &label} 238 _, _, c, err = t.call(t.serviceName+".GetCounter", r, wantCounter) 239 return 240 } 241 242 func (t *RPC) RollbackProtectedSeal(label string, data []byte, policy string) (sealed []byte, err error) { 243 r := &RPCRequest{Label: &label, Data: data, Policy: &policy} 244 sealed, _, _, err = t.call(t.serviceName+".RollbackProtectedSeal", r, wantData) 245 return 246 } 247 248 func (t *RPC) RollbackProtectedUnseal(sealed []byte) (data []byte, policy string, err error) { 249 r := &RPCRequest{Data: sealed} 250 data, policy, _, err = t.call(t.serviceName+".RollbackProtectedUnseal", r, wantData|wantPolicy) 251 return 252 }