• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2018 The gRPC Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package io.grpc.alts.internal;
18 
19 import static com.google.common.base.Preconditions.checkState;
20 
21 import com.google.common.annotations.VisibleForTesting;
22 import com.google.common.base.Preconditions;
23 import io.grpc.alts.internal.HandshakerServiceGrpc.HandshakerServiceStub;
24 import io.netty.buffer.ByteBufAllocator;
25 import java.nio.ByteBuffer;
26 import java.security.GeneralSecurityException;
27 import java.util.ArrayList;
28 import java.util.List;
29 
30 /**
31  * Negotiates a grpc channel key to be used by the TsiFrameProtector, using ALTs handshaker service.
32  */
33 public final class AltsTsiHandshaker implements TsiHandshaker {
34   public static final String TSI_SERVICE_ACCOUNT_PEER_PROPERTY = "service_account";
35 
36   private final boolean isClient;
37   private final AltsHandshakerClient handshaker;
38 
39   private ByteBuffer outputFrame;
40 
41   /** Starts a new TSI handshaker with client options. */
AltsTsiHandshaker( boolean isClient, HandshakerServiceStub stub, AltsHandshakerOptions options)42   private AltsTsiHandshaker(
43       boolean isClient, HandshakerServiceStub stub, AltsHandshakerOptions options) {
44     this.isClient = isClient;
45     handshaker = new AltsHandshakerClient(stub, options);
46   }
47 
48   @VisibleForTesting
AltsTsiHandshaker(boolean isClient, AltsHandshakerClient handshaker)49   AltsTsiHandshaker(boolean isClient, AltsHandshakerClient handshaker) {
50     this.isClient = isClient;
51     this.handshaker = handshaker;
52   }
53 
54   /**
55    * Process the bytes received from the peer.
56    *
57    * @param bytes The buffer containing the handshake bytes from the peer.
58    * @return true, if the handshake has all the data it needs to process and false, if the method
59    *     must be called again to complete processing.
60    */
61   @Override
processBytesFromPeer(ByteBuffer bytes)62   public boolean processBytesFromPeer(ByteBuffer bytes) throws GeneralSecurityException {
63     // If we're the client and we haven't given an output frame, we shouldn't be processing any
64     // bytes.
65     if (outputFrame == null && isClient) {
66       return true;
67     }
68     // If we already have bytes to write, just return.
69     if (outputFrame != null && outputFrame.hasRemaining()) {
70       return true;
71     }
72     int remaining = bytes.remaining();
73     // Call handshaker service to proceess the bytes.
74     if (outputFrame == null) {
75       checkState(!isClient, "Client handshaker should not process any frame at the beginning.");
76       outputFrame = handshaker.startServerHandshake(bytes);
77     } else {
78       outputFrame = handshaker.next(bytes);
79     }
80     // If handshake has finished or we already have bytes to write, just return true.
81     if (handshaker.isFinished() || outputFrame.hasRemaining()) {
82       return true;
83     }
84     // We have done processing input bytes, but no bytes to write. Thus we need more data.
85     if (!bytes.hasRemaining()) {
86       return false;
87     }
88     // There are still remaining bytes. Thus we need to continue processing the bytes.
89     // Prevent infinite loop by checking some bytes are consumed by handshaker.
90     checkState(bytes.remaining() < remaining, "Handshaker did not consume any bytes.");
91     return processBytesFromPeer(bytes);
92   }
93 
94   /**
95    * Returns the peer extracted from a completed handshake.
96    *
97    * @return the extracted peer.
98    */
99   @Override
100   public TsiPeer extractPeer() throws GeneralSecurityException {
101     Preconditions.checkState(!isInProgress(), "Handshake is not complete.");
102     List<TsiPeer.Property<?>> peerProperties = new ArrayList<TsiPeer.Property<?>>();
103     peerProperties.add(
104         new TsiPeer.StringProperty(
105             TSI_SERVICE_ACCOUNT_PEER_PROPERTY,
106             handshaker.getResult().getPeerIdentity().getServiceAccount()));
107     return new TsiPeer(peerProperties);
108   }
109 
110   /**
111    * Returns the peer extracted from a completed handshake.
112    *
113    * @return the extracted peer.
114    */
115   @Override
116   public Object extractPeerObject() throws GeneralSecurityException {
117     Preconditions.checkState(!isInProgress(), "Handshake is not complete.");
118     return new AltsAuthContext(handshaker.getResult());
119   }
120 
121   /** Creates a new TsiHandshaker for use by the client. */
122   public static TsiHandshaker newClient(HandshakerServiceStub stub, AltsHandshakerOptions options) {
123     return new AltsTsiHandshaker(true, stub, options);
124   }
125 
126   /** Creates a new TsiHandshaker for use by the server. */
127   public static TsiHandshaker newServer(HandshakerServiceStub stub, AltsHandshakerOptions options) {
128     return new AltsTsiHandshaker(false, stub, options);
129   }
130 
131   /**
132    * Gets bytes that need to be sent to the peer.
133    *
134    * @param bytes The buffer to put handshake bytes.
135    */
136   @Override
137   public void getBytesToSendToPeer(ByteBuffer bytes) throws GeneralSecurityException {
138     if (outputFrame == null) { // A null outputFrame indicates we haven't started the handshake.
139       if (isClient) {
140         outputFrame = handshaker.startClientHandshake();
141       } else {
142         // The server needs bytes to process before it can start the handshake.
143         return;
144       }
145     }
146     // Write as many bytes as we are able.
147     ByteBuffer outputFrameAlias = outputFrame;
148     if (outputFrame.remaining() > bytes.remaining()) {
149       outputFrameAlias = outputFrame.duplicate();
150       outputFrameAlias.limit(outputFrameAlias.position() + bytes.remaining());
151     }
152     bytes.put(outputFrameAlias);
153     outputFrame.position(outputFrameAlias.position());
154   }
155 
156   /**
157    * Returns true if and only if the handshake is still in progress
158    *
159    * @return true, if the handshake is still in progress, false otherwise.
160    */
161   @Override
isInProgress()162   public boolean isInProgress() {
163     return !handshaker.isFinished() || outputFrame.hasRemaining();
164   }
165 
166   /**
167    * Creates a frame protector from a completed handshake. No other methods may be called after the
168    * frame protector is created.
169    *
170    * @param maxFrameSize the requested max frame size, the callee is free to ignore.
171    * @param alloc used for allocating ByteBufs.
172    * @return a new TsiFrameProtector.
173    */
174   @Override
createFrameProtector(int maxFrameSize, ByteBufAllocator alloc)175   public TsiFrameProtector createFrameProtector(int maxFrameSize, ByteBufAllocator alloc) {
176     Preconditions.checkState(!isInProgress(), "Handshake is not complete.");
177 
178     byte[] key = handshaker.getKey();
179     Preconditions.checkState(key.length == AltsChannelCrypter.getKeyLength(), "Bad key length.");
180 
181     return new AltsTsiFrameProtector(maxFrameSize, new AltsChannelCrypter(key, isClient), alloc);
182   }
183 
184   /**
185    * Creates a frame protector from a completed handshake. No other methods may be called after the
186    * frame protector is created.
187    *
188    * @param alloc used for allocating ByteBufs.
189    * @return a new TsiFrameProtector.
190    */
191   @Override
createFrameProtector(ByteBufAllocator alloc)192   public TsiFrameProtector createFrameProtector(ByteBufAllocator alloc) {
193     return createFrameProtector(AltsTsiFrameProtector.getMaxAllowedFrameBytes(), alloc);
194   }
195 }
196