Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/prio/PrioEncoder.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "mozilla/ClearOnShutdown.h"
8
#include "mozilla/Preferences.h"
9
#include "mozilla/ScopeExit.h"
10
#include "mozilla/Services.h"
11
#include "mozilla/TextUtils.h"
12
13
#include "mozilla/dom/ToJSValue.h"
14
15
#include "PrioEncoder.h"
16
17
namespace mozilla {
18
namespace dom {
19
20
/* static */ StaticRefPtr<PrioEncoder> PrioEncoder::sSingleton;
21
22
/* static */ PublicKey PrioEncoder::sPublicKeyA = nullptr;
23
/* static */ PublicKey PrioEncoder::sPublicKeyB = nullptr;
24
25
0
PrioEncoder::PrioEncoder() = default;
26
PrioEncoder::~PrioEncoder()
27
0
{
28
0
  if (sPublicKeyA) {
29
0
    PublicKey_clear(sPublicKeyA);
30
0
    sPublicKeyA = nullptr;
31
0
  }
32
0
33
0
  if (sPublicKeyB) {
34
0
    PublicKey_clear(sPublicKeyB);
35
0
    sPublicKeyB = nullptr;
36
0
  }
37
0
38
0
  Prio_clear();
39
0
}
40
41
/* static */ void
42
PrioEncoder::Encode(GlobalObject& aGlobal,
43
                    const nsCString& aBatchID,
44
                    const PrioParams& aPrioParams,
45
                    RootedDictionary<PrioEncodedData>& aData,
46
                    ErrorResult& aRv)
47
0
{
48
0
  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
49
0
  if (!global) {
50
0
    aRv.Throw(NS_ERROR_UNEXPECTED);
51
0
    return;
52
0
  }
53
0
54
0
  SECStatus prio_rv = SECSuccess;
55
0
56
0
  if (!sSingleton) {
57
0
    nsresult rv;
58
0
59
0
    nsAutoCStringN<CURVE25519_KEY_LEN_HEX + 1> prioKeyA;
60
0
    rv = Preferences::GetCString("prio.publicKeyA", prioKeyA);
61
0
    if (NS_FAILED(rv)) {
62
0
      aRv.Throw(NS_ERROR_UNEXPECTED);
63
0
      return;
64
0
    }
65
0
66
0
    nsAutoCStringN<CURVE25519_KEY_LEN_HEX + 1> prioKeyB;
67
0
    rv = Preferences::GetCString("prio.publicKeyB", prioKeyB);
68
0
    if (NS_FAILED(rv)) {
69
0
      aRv.Throw(NS_ERROR_UNEXPECTED);
70
0
      return;
71
0
    }
72
0
73
0
    // Check that both public keys are of the right length
74
0
    // and contain only hex digits 0-9a-fA-f
75
0
    if (!PrioEncoder::IsValidHexPublicKey(prioKeyA)
76
0
        || !PrioEncoder::IsValidHexPublicKey(prioKeyB))  {
77
0
      aRv.Throw(NS_ERROR_UNEXPECTED);
78
0
      return;
79
0
    }
80
0
81
0
    prio_rv = Prio_init();
82
0
83
0
    if (prio_rv != SECSuccess) {
84
0
      aRv.Throw(NS_ERROR_UNEXPECTED);
85
0
      return;
86
0
    }
87
0
88
0
    prio_rv = PublicKey_import_hex(&sPublicKeyA,
89
0
                                   reinterpret_cast<const unsigned char*>(prioKeyA.BeginReading()),
90
0
                                   CURVE25519_KEY_LEN_HEX);
91
0
    if (prio_rv != SECSuccess) {
92
0
      aRv.Throw(NS_ERROR_UNEXPECTED);
93
0
      return;
94
0
    }
95
0
96
0
    prio_rv = PublicKey_import_hex(&sPublicKeyB,
97
0
              reinterpret_cast<const unsigned char*>(prioKeyB.BeginReading()),
98
0
              CURVE25519_KEY_LEN_HEX);
99
0
    if (prio_rv != SECSuccess) {
100
0
      aRv.Throw(NS_ERROR_UNEXPECTED);
101
0
      return;
102
0
    }
103
0
104
0
    sSingleton = new PrioEncoder();
105
0
    ClearOnShutdown(&sSingleton);
106
0
  }
107
0
108
0
  bool dataItems[] = {
109
0
    aPrioParams.mBrowserIsUserDefault,
110
0
    aPrioParams.mNewTabPageEnabled,
111
0
    aPrioParams.mPdfViewerUsed,
112
0
  };
113
0
114
0
  PrioConfig prioConfig = PrioConfig_new(mozilla::ArrayLength(dataItems),
115
0
                                         sPublicKeyA,
116
0
                                         sPublicKeyB,
117
0
                                         reinterpret_cast<const unsigned char*>(aBatchID.BeginReading()),
118
0
                                         aBatchID.Length());
119
0
120
0
  if (!prioConfig) {
121
0
    aRv.Throw(NS_ERROR_FAILURE);
122
0
    return;
123
0
  }
124
0
125
0
  auto configGuard = MakeScopeExit([&] {
126
0
    PrioConfig_clear(prioConfig);
127
0
  });
128
0
129
0
  unsigned char* forServerA = nullptr;
130
0
  unsigned int lenA = 0;
131
0
  unsigned char* forServerB = nullptr;
132
0
  unsigned int lenB = 0;
133
0
134
0
  prio_rv = PrioClient_encode(prioConfig,
135
0
                              dataItems,
136
0
                              &forServerA,
137
0
                              &lenA,
138
0
                              &forServerB,
139
0
                              &lenB);
140
0
141
0
  nsTArray<uint8_t> arrayForServerA;
142
0
  nsTArray<uint8_t> arrayForServerB;
143
0
144
0
  if (!arrayForServerA.AppendElements(reinterpret_cast<uint8_t*>(forServerA),
145
0
                                      lenA,
146
0
                                      fallible)) {
147
0
    aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
148
0
    return;
149
0
  }
150
0
151
0
  free(forServerA);
152
0
153
0
  if (!arrayForServerB.AppendElements(reinterpret_cast<uint8_t*>(forServerB),
154
0
                                      lenB,
155
0
                                      fallible)) {
156
0
    aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
157
0
    return ;
158
0
  }
159
0
160
0
  free(forServerB);
161
0
162
0
  if (prio_rv != SECSuccess) {
163
0
    aRv.Throw(NS_ERROR_FAILURE);
164
0
    return;
165
0
  }
166
0
167
0
  JS::Rooted<JS::Value> valueA(aGlobal.Context());
168
0
  if (!ToJSValue(aGlobal.Context(), TypedArrayCreator<Uint8Array>(arrayForServerA), &valueA)) {
169
0
    aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
170
0
    return;
171
0
  }
172
0
173
0
  aData.mA.Construct().Init(&valueA.toObject());
174
0
175
0
  JS::Rooted<JS::Value> valueB(aGlobal.Context());
176
0
  if (!ToJSValue(aGlobal.Context(), TypedArrayCreator<Uint8Array>(arrayForServerB), &valueB)) {
177
0
    aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
178
0
    return;
179
0
  }
180
0
181
0
  aData.mB.Construct().Init(&valueB.toObject());
182
0
}
183
184
bool
185
PrioEncoder::IsValidHexPublicKey(mozilla::Span<const char> aStr)
186
0
{
187
0
  if (aStr.Length() != CURVE25519_KEY_LEN_HEX) {
188
0
    return false;
189
0
  }
190
0
191
0
  for (auto c : aStr) {
192
0
    if (!IsAsciiHexDigit(c)) {
193
0
      return false;
194
0
    }
195
0
  }
196
0
197
0
  return true;
198
0
}
199
200
} // dom namespace
201
} // mozilla namespace