1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
|
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/web_package/signed_web_bundles/signed_web_bundle_id.h"
#include <algorithm>
#include <ostream>
#include <string_view>
#include "base/containers/span.h"
#include "base/functional/bind.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/types/expected.h"
#include "components/base32/base32.h"
#include "crypto/random.h"
namespace web_package {
// static
base::expected<SignedWebBundleId, std::string> SignedWebBundleId::Create(
std::string_view encoded_id) {
if (encoded_id.size() != kEd25519EncodedIdLength &&
encoded_id.size() != kEcdsaP256EncodedIdLength) {
return base::unexpected(base::StringPrintf(
"The signed web bundle ID must be exactly %zu "
"characters long (for Ed25519) or %zu characters long (for ECDSA "
"P-256), but was %zu characters long.",
kEd25519EncodedIdLength, kEcdsaP256EncodedIdLength, encoded_id.size()));
}
for (const char c : encoded_id) {
if (!(base::IsAsciiLower(c) || (c >= '2' && c <= '7'))) {
return base::unexpected(
"The signed web bundle ID must only contain lowercase ASCII "
"characters and digits between 2 and 7 (without any padding).");
}
}
// Base32 decode the ID as an array.
const std::vector<uint8_t> decoded_id =
base32::Base32Decode(base::ToUpperASCII(encoded_id));
if (decoded_id.size() < kTypeSuffixLength) {
return base::unexpected(
"The signed web bundle ID could not be decoded from its base32 "
"representation.");
}
auto type_suffix = base::span(decoded_id).last<kTypeSuffixLength>();
if (std::ranges::equal(type_suffix, kTypeProxyMode)) {
if (decoded_id.size() == kProxyModeDecodedIdLength) {
return SignedWebBundleId(Type::kProxyMode, encoded_id);
} else {
return base::unexpected(base::StringPrintf(
"A ProxyMode signed web bundle ID must be exactly %zu "
"characters long, but was %zu characters long.",
kProxyModeEncodedIdLength, encoded_id.size()));
}
}
if (std::ranges::equal(type_suffix, kTypeEd25519PublicKey)) {
if (decoded_id.size() == kEd25519DecodedIdLength) {
return SignedWebBundleId(Type::kEd25519PublicKey, encoded_id);
} else {
return base::unexpected(base::StringPrintf(
"An Ed25519 signed web bundle ID must be exactly %zu "
"characters long, but was %zu characters long.",
kEd25519EncodedIdLength, encoded_id.size()));
}
}
if (std::ranges::equal(type_suffix, kTypeEcdsaP256PublicKey)) {
if (decoded_id.size() == kEcdsaP256DecodedIdLength) {
return SignedWebBundleId(Type::kEcdsaP256PublicKey, encoded_id);
} else {
return base::unexpected(base::StringPrintf(
"An ECDSA P-256 signed web bundle ID must be exactly %zu "
"characters long, but was %zu characters long.",
kEcdsaP256EncodedIdLength, encoded_id.size()));
}
}
return base::unexpected("The signed web bundle ID has an unknown type.");
}
// static
SignedWebBundleId SignedWebBundleId::CreateForPublicKey(
const Ed25519PublicKey& public_key) {
std::array<uint8_t, kEd25519DecodedIdLength> decoded_id;
std::ranges::copy(public_key.bytes(), decoded_id.begin());
std::ranges::copy(kTypeEd25519PublicKey,
decoded_id.end() - kTypeSuffixLength);
auto encoded_id_uppercase = base32::Base32Encode(
decoded_id, base32::Base32EncodePolicy::OMIT_PADDING);
auto encoded_id = base::ToLowerASCII(encoded_id_uppercase);
return SignedWebBundleId(Type::kEd25519PublicKey, encoded_id);
}
// static
SignedWebBundleId SignedWebBundleId::CreateForPublicKey(
const EcdsaP256PublicKey& public_key) {
std::array<uint8_t, kEcdsaP256DecodedIdLength> decoded_id;
std::ranges::copy(public_key.bytes(), decoded_id.begin());
std::ranges::copy(kTypeEcdsaP256PublicKey,
decoded_id.end() - kTypeSuffixLength);
auto encoded_id_uppercase = base32::Base32Encode(
decoded_id, base32::Base32EncodePolicy::OMIT_PADDING);
auto encoded_id = base::ToLowerASCII(encoded_id_uppercase);
return SignedWebBundleId(Type::kEcdsaP256PublicKey, encoded_id);
}
// static
SignedWebBundleId SignedWebBundleId::CreateForProxyMode(
base::span<const uint8_t, kProxyModeKeyLength> data) {
std::array<uint8_t, kProxyModeKeyLength + kTypeSuffixLength> decoded_id;
std::ranges::copy(data, decoded_id.begin());
std::ranges::copy(kTypeProxyMode, decoded_id.end() - kTypeSuffixLength);
auto encoded_id_uppercase = base32::Base32Encode(
decoded_id, base32::Base32EncodePolicy::OMIT_PADDING);
auto encoded_id = base::ToLowerASCII(encoded_id_uppercase);
return SignedWebBundleId(Type::kProxyMode, encoded_id);
}
// static
SignedWebBundleId SignedWebBundleId::CreateRandomForProxyMode() {
std::array<uint8_t, kProxyModeKeyLength> random_bytes;
crypto::RandBytes(random_bytes);
return CreateForProxyMode(random_bytes);
}
SignedWebBundleId::SignedWebBundleId(Type type, std::string_view encoded_id)
: type_(type), encoded_id_(encoded_id) {}
SignedWebBundleId::SignedWebBundleId(const SignedWebBundleId& other) = default;
SignedWebBundleId& SignedWebBundleId::operator=(
const SignedWebBundleId& other) = default;
SignedWebBundleId::~SignedWebBundleId() = default;
std::ostream& operator<<(std::ostream& os, const SignedWebBundleId& id) {
return os << id.id();
}
} // namespace web_package
|