• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 Google LLC
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 //     https://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 com.google.security.cryptauth.lib.securegcm;
16 
17 import java.io.ByteArrayOutputStream;
18 import java.io.IOException;
19 import java.security.MessageDigest;
20 import java.security.NoSuchAlgorithmException;
21 import java.util.Arrays;
22 import javax.crypto.SecretKey;
23 
24 /**
25  * Implementation of {@link D2DConnectionContext} for version 1 of the D2D protocol. In this
26  * version, communication is fully duplex, as separate keys and sequence nubmers are used for
27  * encoding and decoding.
28  */
29 public class D2DConnectionContextV1 extends D2DConnectionContext {
30   public static final int PROTOCOL_VERSION = 1;
31 
32   private final SecretKey encodeKey;
33   private final SecretKey decodeKey;
34   private int encodeSequenceNumber;
35   private int decodeSequenceNumber;
36 
37   /**
38    * Package private constructor. Should never be called directly except by the
39    * {@link D2DHandshakeContext}
40    *
41    * @param encodeKey
42    * @param decodeKey
43    * @param initialEncodeSequenceNumber
44    * @param initialDecodeSequenceNumber
45    */
D2DConnectionContextV1( SecretKey encodeKey, SecretKey decodeKey, int initialEncodeSequenceNumber, int initialDecodeSequenceNumber)46   D2DConnectionContextV1(
47       SecretKey encodeKey,
48       SecretKey decodeKey,
49       int initialEncodeSequenceNumber,
50       int initialDecodeSequenceNumber) {
51     super(PROTOCOL_VERSION);
52     this.encodeKey = encodeKey;
53     this.decodeKey = decodeKey;
54     this.encodeSequenceNumber = initialEncodeSequenceNumber;
55     this.decodeSequenceNumber = initialDecodeSequenceNumber;
56   }
57 
58   @Override
getSessionUnique()59   public byte[] getSessionUnique() throws NoSuchAlgorithmException {
60     if (encodeKey == null || decodeKey == null) {
61       throw new IllegalStateException(
62           "Connection has not been correctly initialized; encode key or decode key is null");
63     }
64 
65     // Ensure that the initator and responder keys are hashed in a deterministic order, so they have
66     // the same session unique code.
67     byte[] encodeKeyBytes = encodeKey.getEncoded();
68     byte[] decodeKeyBytes = decodeKey.getEncoded();
69     int encodeKeyHash = Arrays.hashCode(encodeKeyBytes);
70     int decodeKeyHash = Arrays.hashCode(decodeKeyBytes);
71     byte[] firstKeyBytes = encodeKeyHash < decodeKeyHash ? encodeKeyBytes : decodeKeyBytes;
72     byte[] secondKeyBytes = firstKeyBytes == encodeKeyBytes ? decodeKeyBytes : encodeKeyBytes;
73 
74     MessageDigest md = MessageDigest.getInstance("SHA-256");
75     md.update(D2DCryptoOps.SALT);
76     md.update(firstKeyBytes);
77     md.update(secondKeyBytes);
78     return md.digest();
79   }
80 
81   @Override
82   protected void incrementSequenceNumberForEncoding() {
83     encodeSequenceNumber++;
84   }
85 
86   @Override
87   protected void incrementSequenceNumberForDecoding() {
88     decodeSequenceNumber++;
89   }
90 
91   @Override
92   int getSequenceNumberForEncoding() {
93     return encodeSequenceNumber;
94   }
95 
96   @Override
97   int getSequenceNumberForDecoding() {
98     return decodeSequenceNumber;
99   }
100 
101   @Override
102   SecretKey getEncodeKey() {
103     return encodeKey;
104   }
105 
106   @Override
107   SecretKey getDecodeKey() {
108     return decodeKey;
109   }
110 
111   /**
112    * Structure of saved session is:
113    * +------------------------------------------------------------------------------------------+
114    * |     1 Byte       | 4 Bytes (big endian) | 4 Bytes (big endian) |  32 Bytes  |  32 Bytes  |
115    * +------------------------------------------------------------------------------------------+
116    * | Protocol Version |   encode seq number  |   decode seq number  | encode key | decode key |
117    * +------------------------------------------------------------------------------------------+
118    */
119   @Override
120   public byte[] saveSession() {
121     ByteArrayOutputStream bytes = new ByteArrayOutputStream();
122 
123     try {
124       // Protocol version
125       bytes.write(1);
126 
127       // Encode sequence number
128       bytes.write(signedIntToBytes(encodeSequenceNumber));
129 
130       // Decode sequence number
131       bytes.write(signedIntToBytes(decodeSequenceNumber));
132 
133       // Encode Key
134       bytes.write(encodeKey.getEncoded());
135 
136       // Decode Key
137       bytes.write(decodeKey.getEncoded());
138     } catch (IOException e) {
139       // should not happen
140       e.printStackTrace();
141       return null;
142     }
143 
144     return bytes.toByteArray();
145   }
146 }
147