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