xref: /aosp_15_r20/external/federated-compute/fcp/secagg/shared/aes_key.cc (revision 14675a029014e728ec732f129a32e299b2da0601)
1 // Copyright 2021 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 //      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 #include "fcp/secagg/shared/aes_key.h"
16 
17 #include <string>
18 
19 #include "fcp/base/monitoring.h"
20 #include "fcp/secagg/shared/shamir_secret_sharing.h"
21 
22 static constexpr int kLegacyKeySize = 17;
23 
24 namespace fcp {
25 namespace secagg {
26 
AesKey(const uint8_t * data,int key_size)27 AesKey::AesKey(const uint8_t* data, int key_size) : Key(data, key_size) {
28   FCP_CHECK((key_size > 0 && key_size <= 17) || (key_size == 32));
29 }
30 
CreateFromShares(const std::vector<ShamirShare> & shares,int threshold)31 StatusOr<AesKey> AesKey::CreateFromShares(
32     const std::vector<ShamirShare>& shares, int threshold) {
33   ShamirSecretSharing reconstructor;
34   // TODO(team): Once Java support is removed, assume 32 byte keys.
35   int key_length = 0;
36   // For compatibility, we need to know if the key that was shared was 128 or
37   // 256 bits long. It can only have been one of those two lengths, so the
38   // shares should be either 20 or 36 bytes long respectively.
39   for (int i = 0; i < shares.size() && key_length == 0; ++i) {
40     if (shares[i].data.size() == 36) {
41       key_length = kSize;
42     } else if (shares[i].data.size() == 20) {
43       key_length = kLegacyKeySize;  // May be 17 bytes or shorter, see below
44     } else {
45       // Key share must be missing if it's not one of those lengths.
46       FCP_CHECK(shares[i].data.empty());
47     }
48   }
49   FCP_CHECK(key_length != 0);
50   std::string reconstructed;
51   FCP_ASSIGN_OR_RETURN(
52       reconstructed, reconstructor.Reconstruct(threshold, shares, key_length));
53 
54   if (key_length == kLegacyKeySize) {
55     // The key produced on Java side normally has 16 bytes, however when
56     // exporting the key from BigInteger to byte array an extra zero byte is
57     // added at the front if the high-order bit was '1' to indicate that the
58     // BigInteger value was positive (to avoid treating the high order bit
59     // as the sign bit). However the byte array may also be shorter than
60     // 16 bytes if the BigInteger value was smaller.
61     // For compatibility with Java behavior any leading zero byte that isn't
62     // followed by a byte with '1' in the high-order bit need to be removed.
63     int index = 0;
64     while (index < kLegacyKeySize - 1 &&
65            static_cast<uint8_t>(reconstructed[index]) == 0 &&
66            static_cast<uint8_t>(reconstructed[index + 1]) <= 127) {
67       index++;
68     }
69 
70     if (index > 0) {
71       reconstructed.erase(0, index);
72       key_length -= index;
73     }
74   }
75   return AesKey(reinterpret_cast<const uint8_t*>(reconstructed.c_str()),
76                 key_length);
77 }
78 
79 }  // namespace secagg
80 }  // namespace fcp
81