SHELL: Support component build for SHELL

This CL is adding component build support for SHELL library.
That change is needed in order for the library to be used by
any number of consumers. That will be needed in order for
the private_membership library to have its component build
as well.

This CL is doing the following:
1) Allowing for the library to use component macros needed to
export the classes symbol correctly
2) Adding a patch set with all these library's changes
3) Adding explicit definitions for the generic templates classes

Also, these changes will be modified later on the upstream
with correct macro that will be mapped to empty. That is needed
now in order to unblock another team to use the private_membership
library to avoid further delay for their launch.

Bug: 1241832, b/201763017

Change-Id: I651d50ae579bd72a5459a59892549ec4eb250575
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3225925
Commit-Queue: Amr Aboelkher <amraboelkher@chromium.org>
Reviewed-by: Mirko Bonadei <mbonadei@chromium.org>
Cr-Commit-Position: refs/heads/main@{#943022}
NOKEYCHECK=True
GitOrigin-RevId: f2bcadd8fbfe76a0bc7169693393b2086863006a
diff --git a/BUILD.gn b/BUILD.gn
index 9109500..2f92906 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -15,6 +15,17 @@
     "-Wno-ignored-qualifiers",
   ]
 
+  defines = []
+  if (is_component_build) {
+    defines = [
+      # Build targets inside shell-encryption will need this macro to
+      # be defined in order to correctly export symbols when is_component_build
+      # is true.
+      # For more info see: base/shell_encryption_export.h.
+      "SHELL_ENCRYPTION_ENABLE_SYMBOL_EXPORT",
+    ]
+  }
+
   include_dirs = [
     # Allow includes to be prefixed with shell-encryption/src/ in case it is not an
     # immediate subdirectory of the top-level.
@@ -39,20 +50,30 @@
 proto_library("serialization_proto") {
   sources = [ "src/serialization.proto" ]
   proto_in_dir = "src/"
+
+  cc_generator_options = "dllexport_decl=SHELL_ENCRYPTION_EXPORT:"
+  cc_include = "third_party/shell-encryption/base/shell_encryption_export.h"
+  component_build_force_source_set = true
 }
 
 proto_library("coefficient_polynomial_proto") {
   sources = [ "src/testing/coefficient_polynomial.proto" ]
   proto_in_dir = "src/testing/"
+
+  cc_generator_options = "dllexport_decl=SHELL_ENCRYPTION_EXPORT:"
+  cc_include = "third_party/shell-encryption/base/shell_encryption_export.h"
+  component_build_force_source_set = true
 }
 
 # SHELL lib.
 if (is_chromeos_ash) {
-  source_set("shell_encryption") {
+  component("shell_encryption") {
     public_configs = [ ":shell_encryption_config1" ]
     configs -= [ "//build/config/compiler:chromium_code" ]
     configs += [ "//build/config/compiler:no_chromium_code" ]
     public = [
+      "base/shell_encryption_export.h",
+      "base/shell_encryption_export_template.h",
       "glog/logging.h",
       "src/bits_util.h",
       "src/constants.h",
@@ -116,6 +137,7 @@
     ]
     public_deps = [
       ":coefficient_polynomial_proto",
+      ":serialization_proto",
       ":shell_encryption",
       "//testing/gmock:gmock",
       "//testing/gtest:gtest",
diff --git a/base/shell_encryption_export.h b/base/shell_encryption_export.h
new file mode 100644
index 0000000..72670c3
--- /dev/null
+++ b/base/shell_encryption_export.h
@@ -0,0 +1,28 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHELL_ENCRYPTION_BASE_SHELL_ENCRYPTION_EXPORT_H_
+#define SHELL_ENCRYPTION_BASE_SHELL_ENCRYPTION_EXPORT_H_
+
+// SHELL_ENCRYPTION_EXPORT is used to mark symbols as imported or
+// exported when shell-encryption is built or used as a shared library.
+// When shell-encryption is built as a static library the
+// SHELL_ENCRYPTION_EXPORT macro expands to nothing.
+//
+// This export macros doesn't support Windows. There will be additional
+// component build work to support Windows (see crbug.com/1269714).
+
+#ifdef SHELL_ENCRYPTION_ENABLE_SYMBOL_EXPORT
+
+#if __has_attribute(visibility)
+#define SHELL_ENCRYPTION_EXPORT __attribute__((visibility("default")))
+#endif
+
+#endif  // SHELL_ENCRYPTION_ENABLE_SYMBOL_EXPORT
+
+#ifndef SHELL_ENCRYPTION_EXPORT
+#define SHELL_ENCRYPTION_EXPORT
+#endif
+
+#endif  // SHELL_ENCRYPTION_BASE_SHELL_ENCRYPTION_EXPORT_H_
diff --git a/base/shell_encryption_export_template.h b/base/shell_encryption_export_template.h
new file mode 100644
index 0000000..1feb536
--- /dev/null
+++ b/base/shell_encryption_export_template.h
@@ -0,0 +1,152 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHELL_ENCRYPTION_BASE_SHELL_ENCRYPTION_EXPORT_TEMPLATE_H_
+#define SHELL_ENCRYPTION_BASE_SHELL_ENCRYPTION_EXPORT_TEMPLATE_H_
+
+// This was borrowed from Chromium's base/export_template.h.
+//
+// TODO(crbug.com/1271458): Use shell-encryption template macro's name
+// instead of generic Chromium macro names.
+
+// Synopsis
+//
+// This header provides macros for using FOO_EXPORT macros with explicit
+// template instantiation declarations and definitions.
+// Generally, the FOO_EXPORT macros are used at declarations,
+// and GCC requires them to be used at explicit instantiation declarations,
+// but MSVC requires __declspec(dllexport) to be used at the explicit
+// instantiation definitions instead.
+
+// Usage
+//
+// In a header file, write:
+//
+//   extern template class EXPORT_TEMPLATE_DECLARE(FOO_EXPORT) foo<bar>;
+//
+// In a source file, write:
+//
+//   template class EXPORT_TEMPLATE_DEFINE(FOO_EXPORT) foo<bar>;
+
+// Implementation notes
+//
+// On Windows, when building the FOO library (that is, when FOO_EXPORT expands
+// to __declspec(dllexport)), we want the two lines to expand to:
+//
+//     extern template class foo<bar>;
+//     template class FOO_EXPORT foo<bar>;
+//
+// In all other cases (non-Windows, and Windows when using the FOO library (that
+// is when FOO_EXPORT expands to __declspec(dllimport)), we want:
+//
+//     extern template class FOO_EXPORT foo<bar>;
+//     template class foo<bar>;
+//
+// The implementation of this header uses some subtle macro semantics to
+// detect what the provided FOO_EXPORT value was defined as and then
+// to dispatch to appropriate macro definitions.
+
+#define EXPORT_TEMPLATE_DECLARE(foo_export) \
+  EXPORT_TEMPLATE_INVOKE(DECLARE, EXPORT_TEMPLATE_STYLE(foo_export), foo_export)
+#define EXPORT_TEMPLATE_DEFINE(foo_export) \
+  EXPORT_TEMPLATE_INVOKE(DEFINE, EXPORT_TEMPLATE_STYLE(foo_export), foo_export)
+
+// INVOKE is an internal helper macro to perform parameter replacements
+// and token pasting to chain invoke another macro.  E.g.,
+//     EXPORT_TEMPLATE_INVOKE(DECLARE, DEFAULT, FOO_EXPORT)
+// will expand to call
+//     EXPORT_TEMPLATE_DECLARE_DEFAULT(FOO_EXPORT)
+// (but with FOO_EXPORT expanded too).
+#define EXPORT_TEMPLATE_INVOKE(which, style, foo_export) \
+  EXPORT_TEMPLATE_INVOKE_2(which, style, foo_export)
+#define EXPORT_TEMPLATE_INVOKE_2(which, style, foo_export) \
+  EXPORT_TEMPLATE_##which##_##style(foo_export)
+
+// Default style is to apply the FOO_EXPORT macro at declaration sites.
+#define EXPORT_TEMPLATE_DECLARE_DEFAULT(foo_export) foo_export
+#define EXPORT_TEMPLATE_DEFINE_DEFAULT(foo_export)
+
+// The "declspec" style is used when FOO_EXPORT is defined
+// as __declspec(dllexport), which MSVC requires to be used at
+// definition sites instead.
+#define EXPORT_TEMPLATE_DECLARE_EXPORT_DLLEXPORT(foo_export)
+#define EXPORT_TEMPLATE_DEFINE_EXPORT_DLLEXPORT(foo_export) foo_export
+
+// EXPORT_TEMPLATE_STYLE is an internal helper macro that identifies which
+// export style needs to be used for the provided FOO_EXPORT macro definition.
+// "", "__attribute__(...)", and "__declspec(dllimport)" are mapped
+// to "DEFAULT"; while "__declspec(dllexport)" is mapped to "EXPORT_DLLEXPORT".
+// (NaCl headers define "DLLEXPORT" already, else we'd use that.
+// TODO(thakis): Rename once nacl is gone.)
+//
+// It's implemented with token pasting to transform the __attribute__ and
+// __declspec annotations into macro invocations.  E.g., if FOO_EXPORT is
+// defined as "__declspec(dllimport)", it undergoes the following sequence of
+// macro substitutions:
+//     EXPORT_TEMPLATE_STYLE(FOO_EXPORT)
+//     EXPORT_TEMPLATE_STYLE_2(__declspec(dllimport))
+//     EXPORT_TEMPLATE_STYLE_MATCH__declspec(dllimport)
+//     EXPORT_TEMPLATE_STYLE_MATCH_DECLSPEC_dllimport
+//     DEFAULT
+#define EXPORT_TEMPLATE_STYLE(foo_export) EXPORT_TEMPLATE_STYLE_2(foo_export)
+#define EXPORT_TEMPLATE_STYLE_2(foo_export) \
+  EXPORT_TEMPLATE_STYLE_MATCH_foj3FJo5StF0OvIzl7oMxA##foo_export
+
+// Internal helper macros for EXPORT_TEMPLATE_STYLE.
+//
+// XXX: C++ reserves all identifiers containing "__" for the implementation,
+// but "__attribute__" and "__declspec" already contain "__" and the token-paste
+// operator can only add characters; not remove them.  To minimize the risk of
+// conflict with implementations, we include "foj3FJo5StF0OvIzl7oMxA" (a random
+// 128-bit string, encoded in Base64) in the macro name.
+#define EXPORT_TEMPLATE_STYLE_MATCH_foj3FJo5StF0OvIzl7oMxA DEFAULT
+#define EXPORT_TEMPLATE_STYLE_MATCH_foj3FJo5StF0OvIzl7oMxA__attribute__(...) \
+  DEFAULT
+#define EXPORT_TEMPLATE_STYLE_MATCH_foj3FJo5StF0OvIzl7oMxA__declspec(arg) \
+  EXPORT_TEMPLATE_STYLE_MATCH_DECLSPEC_##arg
+
+// Internal helper macros for EXPORT_TEMPLATE_STYLE.
+#define EXPORT_TEMPLATE_STYLE_MATCH_DECLSPEC_dllexport EXPORT_DLLEXPORT
+#define EXPORT_TEMPLATE_STYLE_MATCH_DECLSPEC_dllimport DEFAULT
+
+// Sanity checks.
+//
+// EXPORT_TEMPLATE_TEST uses the same macro invocation pattern as
+// EXPORT_TEMPLATE_DECLARE and EXPORT_TEMPLATE_DEFINE do to check that they're
+// working correctly.  When they're working correctly, the sequence of macro
+// replacements should go something like:
+//
+//     EXPORT_TEMPLATE_TEST(DEFAULT, __declspec(dllimport));
+//
+//     static_assert(EXPORT_TEMPLATE_INVOKE(TEST_DEFAULT,
+//         EXPORT_TEMPLATE_STYLE(__declspec(dllimport)),
+//         __declspec(dllimport)), "__declspec(dllimport)");
+//
+//     static_assert(EXPORT_TEMPLATE_INVOKE(TEST_DEFAULT,
+//         DEFAULT, __declspec(dllimport)), "__declspec(dllimport)");
+//
+//     static_assert(EXPORT_TEMPLATE_TEST_DEFAULT_DEFAULT(
+//         __declspec(dllimport)), "__declspec(dllimport)");
+//
+//     static_assert(true, "__declspec(dllimport)");
+//
+// When they're not working correctly, a syntax error should occur instead.
+#define EXPORT_TEMPLATE_TEST(want, foo_export)                               \
+  static_assert(                                                             \
+      EXPORT_TEMPLATE_INVOKE(TEST_##want, EXPORT_TEMPLATE_STYLE(foo_export), \
+                             foo_export),                                    \
+      #foo_export)
+#define EXPORT_TEMPLATE_TEST_DEFAULT_DEFAULT(...) true
+#define EXPORT_TEMPLATE_TEST_EXPORT_DLLEXPORT_EXPORT_DLLEXPORT(...) true
+
+EXPORT_TEMPLATE_TEST(DEFAULT, );
+EXPORT_TEMPLATE_TEST(DEFAULT, __attribute__((visibility("default"))));
+EXPORT_TEMPLATE_TEST(EXPORT_DLLEXPORT, __declspec(dllexport));
+EXPORT_TEMPLATE_TEST(DEFAULT, __declspec(dllimport));
+
+#undef EXPORT_TEMPLATE_TEST
+#undef EXPORT_TEMPLATE_TEST_DEFAULT_DEFAULT
+#undef EXPORT_TEMPLATE_TEST_EXPORT_DLLEXPORT_EXPORT_DLLEXPORT
+
+#endif  // SHELL_ENCRYPTION_BASE_SHELL_ENCRYPTION_EXPORT_TEMPLATE_H_
diff --git a/patches/0004-Support-SHELL-component-build.patch b/patches/0004-Support-SHELL-component-build.patch
new file mode 100644
index 0000000..55072fa
--- /dev/null
+++ b/patches/0004-Support-SHELL-component-build.patch
@@ -0,0 +1,655 @@
+diff --git a/galois_key.h b/galois_key.h
+index 0b73a63eff4d0..e6295f1082748 100644
+--- a/galois_key.h
++++ b/galois_key.h
+@@ -23,6 +23,8 @@
+ #include "relinearization_key.h"
+ #include "status_macros.h"
+ #include "statusor.h"
++#include "third_party/shell-encryption/base/shell_encryption_export.h"
++#include "third_party/shell-encryption/base/shell_encryption_export_template.h"
+ 
+ namespace rlwe {
+ 
+@@ -41,7 +43,7 @@ namespace rlwe {
+ //
+ // Details can be found in Appendix D.2 of https://eprint.iacr.org/2011/566.pdf
+ template <typename ModularInt>
+-class GaloisKey {
++class EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) GaloisKey {
+  public:
+   // Initializes a GaloisKey based on a SymmetricRlweKey key that can key-switch
+   // two component ciphertexts. A positive log_decomposition_modulus corresponds
+@@ -49,7 +51,7 @@ class GaloisKey {
+   // power of x in the secret key polynomial s(x^substitution_power) that the
+   // ciphertext is encrypted with. The prng_seed is used to generate and encode
+   // the bottom row of the matrix, which consists of random entries.
+-  static rlwe::StatusOr<GaloisKey> Create(
++  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<GaloisKey> Create(
+       const SymmetricRlweKey<ModularInt>& key, absl::string_view prng_seed,
+       Uint64 substitution_power, Uint64 log_decomposition_modulus) {
+     RLWE_ASSIGN_OR_RETURN(auto relinearization_key,
+@@ -88,7 +90,7 @@ class GaloisKey {
+   // SerializedGaloisKey is (2 * num_parts * dimension) where dimension is the
+   // number of digits needed to represent the modulus in base
+   // 2^{log_decomposition_modulus}. Crashes for non-valid input parameters.
+-  static rlwe::StatusOr<GaloisKey> Deserialize(
++  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<GaloisKey> Deserialize(
+       const SerializedGaloisKey& serialized,
+       const typename ModularInt::Params* modulus_params,
+       const NttParameters<ModularInt>* ntt_params) {
+@@ -110,6 +112,14 @@ class GaloisKey {
+   RelinearizationKey<ModularInt> relinearization_key_;
+ };
+ 
++template class EXPORT_TEMPLATE_DECLARE(
++    SHELL_ENCRYPTION_EXPORT) GaloisKey<rlwe::MontgomeryInt<Uint16>>;
++template class EXPORT_TEMPLATE_DECLARE(
++    SHELL_ENCRYPTION_EXPORT) GaloisKey<rlwe::MontgomeryInt<Uint32>>;
++template class EXPORT_TEMPLATE_DECLARE(
++    SHELL_ENCRYPTION_EXPORT) GaloisKey<rlwe::MontgomeryInt<Uint64>>;
++template class EXPORT_TEMPLATE_DECLARE(
++    SHELL_ENCRYPTION_EXPORT) GaloisKey<rlwe::MontgomeryInt<absl::uint128>>;
+ }  //  namespace rlwe
+ 
+ #endif  // RLWE_GALOIS_KEY_H_
+diff --git a/int256.h b/int256.h
+index 540dce2d765b1..4c464a4978412 100644
+--- a/int256.h
++++ b/int256.h
+@@ -18,13 +18,15 @@
+ 
+ #include "absl/numeric/int128.h"
+ #include "integral_types.h"
++#include "third_party/shell-encryption/base/shell_encryption_export.h"
++#include "third_party/shell-encryption/base/shell_encryption_export_template.h"
+ 
+ namespace rlwe {
+ 
+ struct uint256_pod;
+ 
+ // An unsigned 256-bit integer type. Thread-compatible.
+-class uint256 {
++class SHELL_ENCRYPTION_EXPORT uint256 {
+  public:
+   constexpr uint256();
+   constexpr uint256(absl::uint128 top, absl::uint128 bottom);
+@@ -74,11 +76,11 @@ class uint256 {
+   uint256& operator-=(const uint256& b);
+   uint256& operator*=(const uint256& b);
+   // Long division/modulo for uint256.
+-  uint256& operator/=(const uint256& b);
+-  uint256& operator%=(const uint256& b);
++  SHELL_ENCRYPTION_EXPORT uint256& operator/=(const uint256& b);
++  SHELL_ENCRYPTION_EXPORT uint256& operator%=(const uint256& b);
+   uint256 operator++(int);
+   uint256 operator--(int);
+-  uint256& operator<<=(int);
++  SHELL_ENCRYPTION_EXPORT uint256& operator<<=(int);
+   uint256& operator>>=(int);
+   uint256& operator&=(const uint256& b);
+   uint256& operator|=(const uint256& b);
+@@ -90,7 +92,7 @@ class uint256 {
+   friend absl::uint128 Uint256High128(const uint256& v);
+ 
+   // We add "std::" to avoid including all of port.h.
+-  friend std::ostream& operator<<(std::ostream& o, const uint256& b);
++  friend SHELL_ENCRYPTION_EXPORT std::ostream& operator<<(std::ostream& o, const uint256& b);
+ 
+  private:
+   static void DivModImpl(uint256 dividend, uint256 divisor,
+@@ -121,7 +123,7 @@ constexpr uint256 Uint256Max() {
+ 
+ // This is a POD form of uint256 which can be used for static variables which
+ // need to be operated on as uint256.
+-struct uint256_pod {
++struct SHELL_ENCRYPTION_EXPORT uint256_pod {
+   // Note: The ordering of fields is different than 'class uint256' but the
+   // same as its 2-arg constructor.  This enables more obvious initialization
+   // of static instances, which is the primary reason for this struct in the
+diff --git a/montgomery.cc b/montgomery.cc
+index 9fbc5dabee18c..4bd03362420fc 100644
+--- a/montgomery.cc
++++ b/montgomery.cc
+@@ -14,6 +14,8 @@
+ 
+ #include "montgomery.h"
+ 
++#include "third_party/shell-encryption/base/shell_encryption_export.h"
++#include "third_party/shell-encryption/base/shell_encryption_export_template.h"
+ #include "transcription.h"
+ 
+ namespace rlwe {
+@@ -416,12 +418,12 @@ MontgomeryInt<T> MontgomeryInt<T>::MultiplicativeInverse(
+ 
+ // Instantiations of MontgomeryInt and MontgomeryIntParams with specific
+ // integral types.
+-template struct MontgomeryIntParams<Uint16>;
+-template struct MontgomeryIntParams<Uint32>;
+-template struct MontgomeryIntParams<Uint64>;
+-template struct MontgomeryIntParams<absl::uint128>;
+-template class MontgomeryInt<Uint16>;
+-template class MontgomeryInt<Uint32>;
+-template class MontgomeryInt<Uint64>;
+-template class MontgomeryInt<absl::uint128>;
++template struct EXPORT_TEMPLATE_DEFINE(SHELL_ENCRYPTION_EXPORT) MontgomeryIntParams<Uint16>;
++template struct EXPORT_TEMPLATE_DEFINE(SHELL_ENCRYPTION_EXPORT) MontgomeryIntParams<Uint32>;
++template struct EXPORT_TEMPLATE_DEFINE(SHELL_ENCRYPTION_EXPORT) MontgomeryIntParams<Uint64>;
++template struct EXPORT_TEMPLATE_DEFINE(SHELL_ENCRYPTION_EXPORT) MontgomeryIntParams<absl::uint128>;
++template class EXPORT_TEMPLATE_DEFINE(SHELL_ENCRYPTION_EXPORT) MontgomeryInt<Uint16>;
++template class EXPORT_TEMPLATE_DEFINE(SHELL_ENCRYPTION_EXPORT) MontgomeryInt<Uint32>;
++template class EXPORT_TEMPLATE_DEFINE(SHELL_ENCRYPTION_EXPORT) MontgomeryInt<Uint64>;
++template class EXPORT_TEMPLATE_DEFINE(SHELL_ENCRYPTION_EXPORT) MontgomeryInt<absl::uint128>;
+ }  // namespace rlwe
+diff --git a/montgomery.h b/montgomery.h
+index 4f0e2eafb815f..c3988ff34da34 100644
+--- a/montgomery.h
++++ b/montgomery.h
+@@ -34,6 +34,8 @@
+ #include "serialization.pb.h"
+ #include "status_macros.h"
+ #include "statusor.h"
++#include "third_party/shell-encryption/base/shell_encryption_export.h"
++#include "third_party/shell-encryption/base/shell_encryption_export_template.h"
+ 
+ namespace rlwe {
+ 
+@@ -43,24 +45,24 @@ namespace internal {
+ template <typename T>
+ struct BigInt;
+ // Specialization for uint8, uint16, uint32, uint64, and uint128.
+-template <>
+-struct BigInt<Uint8> {
++template <> 
++struct EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) BigInt<Uint8> {
+   typedef Uint16 value_type;
+ };
+-template <>
+-struct BigInt<Uint16> {
++template <> 
++struct EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) BigInt<Uint16> {
+   typedef Uint32 value_type;
+ };
+-template <>
+-struct BigInt<Uint32> {
++template <> 
++struct EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) BigInt<Uint32> {
+   typedef Uint64 value_type;
+ };
+-template <>
+-struct BigInt<Uint64> {
++template <> 
++struct EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) BigInt<Uint64> {
+   typedef absl::uint128 value_type;
+ };
+-template <>
+-struct BigInt<absl::uint128> {
++template <> 
++struct EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) BigInt<absl::uint128> {
+   typedef uint256 value_type;
+ };
+ 
+@@ -69,7 +71,7 @@ struct BigInt<absl::uint128> {
+ // The parameters necessary for a Montgomery integer. Note that the template
+ // parameters ensure that T is an unsigned integral of at least 8 bits.
+ template <typename T>
+-struct MontgomeryIntParams {
++struct EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) MontgomeryIntParams{
+   // Expose Int and its greater type. BigInt is required in order to multiply
+   // two Int and ensure that no overflow occurs.
+   //
+@@ -159,7 +161,7 @@ struct MontgomeryIntParams {
+   // modulus must be odd.
+   // Returns a tuple of (inv_r, inv_modulus) such that:
+   //     r * inv_r - modulus * inv_modulus = 1
+-  static std::tuple<Int, Int> Inverses(BigInt modulus_bigint, BigInt r);
++  static SHELL_ENCRYPTION_EXPORT std::tuple<Int, Int> Inverses(BigInt modulus_bigint, BigInt r);
+ };
+ 
+ // Stores an integer in Montgomery representation. The goal of this
+@@ -170,7 +172,7 @@ struct MontgomeryIntParams {
+ // The underlying integer type T must be unsigned and must not be bool.
+ // This class is thread safe.
+ template <typename T>
+-class ABSL_MUST_USE_RESULT MontgomeryInt {
++class EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) ABSL_MUST_USE_RESULT MontgomeryInt {
+  public:
+   // Expose Int and its greater type. BigInt is required in order to multiply
+   // two Int and ensure that no overflow occurs. This should also be used by
+@@ -184,16 +186,16 @@ class ABSL_MUST_USE_RESULT MontgomeryInt {
+   // Static factory that converts a non-Montgomery representation integer, the
+   // underlying integer type, into a Montgomery representation integer. Does not
+   // take ownership of params. i.e., import "a".
+-  static rlwe::StatusOr<MontgomeryInt> ImportInt(Int n, const Params* params);
++  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<MontgomeryInt> ImportInt(Int n, const Params* params);
+ 
+   // Static functions to create a MontgomeryInt of 0 and 1.
+-  static MontgomeryInt ImportZero(const Params* params);
+-  static MontgomeryInt ImportOne(const Params* params);
++  static SHELL_ENCRYPTION_EXPORT MontgomeryInt ImportZero(const Params* params);
++  static SHELL_ENCRYPTION_EXPORT MontgomeryInt ImportOne(const Params* params);
+ 
+   // Import a random integer using entropy from specified prng. Does not take
+   // ownership of params or prng.
+   template <typename Prng = rlwe::SecurePrng>
+-  static rlwe::StatusOr<MontgomeryInt> ImportRandom(Prng* prng,
++  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<MontgomeryInt> ImportRandom(Prng* prng,
+                                                     const Params* params) {
+     // In order to generate unbiased randomness, we uniformly and randomly
+     // sample integers in [0, 2^params->log_modulus) until the generated integer
+@@ -234,13 +236,13 @@ class ABSL_MUST_USE_RESULT MontgomeryInt {
+ 
+   // Serialization.
+   rlwe::StatusOr<std::string> Serialize(const Params* params) const;
+-  static rlwe::StatusOr<std::string> SerializeVector(
++  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::string> SerializeVector(
+       const std::vector<MontgomeryInt>& coeffs, const Params* params);
+ 
+   // Deserialization.
+-  static rlwe::StatusOr<MontgomeryInt> Deserialize(absl::string_view payload,
++  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<MontgomeryInt> Deserialize(absl::string_view payload,
+                                                    const Params* params);
+-  static rlwe::StatusOr<std::vector<MontgomeryInt>> DeserializeVector(
++  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::vector<MontgomeryInt>> DeserializeVector(
+       int num_coeffs, absl::string_view serialized, const Params* params);
+ 
+   // Modular multiplication.
+@@ -353,7 +355,7 @@ class ABSL_MUST_USE_RESULT MontgomeryInt {
+   // size.
+ 
+   // Batch addition of two vectors.
+-  static rlwe::StatusOr<std::vector<MontgomeryInt>> BatchAdd(
++  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::vector<MontgomeryInt>> BatchAdd(
+       const std::vector<MontgomeryInt>& in1,
+       const std::vector<MontgomeryInt>& in2, const Params* params);
+   static absl::Status BatchAddInPlace(std::vector<MontgomeryInt>* in1,
+@@ -361,7 +363,7 @@ class ABSL_MUST_USE_RESULT MontgomeryInt {
+                                       const Params* params);
+ 
+   // Batch addition of one vector with a scalar.
+-  static rlwe::StatusOr<std::vector<MontgomeryInt>> BatchAdd(
++  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::vector<MontgomeryInt>> BatchAdd(
+       const std::vector<MontgomeryInt>& in1, const MontgomeryInt& in2,
+       const Params* params);
+   static absl::Status BatchAddInPlace(std::vector<MontgomeryInt>* in1,
+@@ -369,51 +371,51 @@ class ABSL_MUST_USE_RESULT MontgomeryInt {
+                                       const Params* params);
+ 
+   // Batch subtraction of two vectors.
+-  static rlwe::StatusOr<std::vector<MontgomeryInt>> BatchSub(
++  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::vector<MontgomeryInt>> BatchSub(
+       const std::vector<MontgomeryInt>& in1,
+       const std::vector<MontgomeryInt>& in2, const Params* params);
+-  static absl::Status BatchSubInPlace(std::vector<MontgomeryInt>* in1,
++  static SHELL_ENCRYPTION_EXPORT absl::Status BatchSubInPlace(std::vector<MontgomeryInt>* in1,
+                                       const std::vector<MontgomeryInt>& in2,
+                                       const Params* params);
+ 
+   // Batch subtraction of one vector with a scalar.
+-  static rlwe::StatusOr<std::vector<MontgomeryInt>> BatchSub(
++  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::vector<MontgomeryInt>> BatchSub(
+       const std::vector<MontgomeryInt>& in1, const MontgomeryInt& in2,
+       const Params* params);
+-  static absl::Status BatchSubInPlace(std::vector<MontgomeryInt>* in1,
++  static SHELL_ENCRYPTION_EXPORT absl::Status BatchSubInPlace(std::vector<MontgomeryInt>* in1,
+                                       const MontgomeryInt& in2,
+                                       const Params* params);
+ 
+   // Batch multiplication of two vectors.
+-  static rlwe::StatusOr<std::vector<MontgomeryInt>> BatchMul(
++  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::vector<MontgomeryInt>> BatchMul(
+       const std::vector<MontgomeryInt>& in1,
+       const std::vector<MontgomeryInt>& in2, const Params* params);
+-  static absl::Status BatchMulInPlace(std::vector<MontgomeryInt>* in1,
++  static SHELL_ENCRYPTION_EXPORT absl::Status BatchMulInPlace(std::vector<MontgomeryInt>* in1,
+                                       const std::vector<MontgomeryInt>& in2,
+                                       const Params* params);
+ 
+   // Batch multiplication of two vectors, where the second vector is a constant.
+-  static rlwe::StatusOr<std::vector<MontgomeryInt>> BatchMulConstant(
++  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::vector<MontgomeryInt>> BatchMulConstant(
+       const std::vector<MontgomeryInt>& in1,
+       const std::vector<Int>& in2_constant,
+       const std::vector<Int>& in2_constant_barrett, const Params* params);
+-  static absl::Status BatchMulConstantInPlace(
++  static SHELL_ENCRYPTION_EXPORT absl::Status BatchMulConstantInPlace(
+       std::vector<MontgomeryInt>* in1, const std::vector<Int>& in2_constant,
+       const std::vector<Int>& in2_constant_barrett, const Params* params);
+ 
+   // Batch multiplication of a vector with a scalar.
+-  static rlwe::StatusOr<std::vector<MontgomeryInt>> BatchMul(
++  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::vector<MontgomeryInt>> BatchMul(
+       const std::vector<MontgomeryInt>& in1, const MontgomeryInt& in2,
+       const Params* params);
+-  static absl::Status BatchMulInPlace(std::vector<MontgomeryInt>* in1,
++  static SHELL_ENCRYPTION_EXPORT absl::Status BatchMulInPlace(std::vector<MontgomeryInt>* in1,
+                                       const MontgomeryInt& in2,
+                                       const Params* params);
+ 
+   // Batch multiplication of a vector with a constant scalar.
+-  static rlwe::StatusOr<std::vector<MontgomeryInt>> BatchMulConstant(
++  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::vector<MontgomeryInt>> BatchMulConstant(
+       const std::vector<MontgomeryInt>& in1, const Int& constant,
+       const Int& constant_barrett, const Params* params);
+-  static absl::Status BatchMulConstantInPlace(std::vector<MontgomeryInt>* in1,
++  static SHELL_ENCRYPTION_EXPORT absl::Status BatchMulConstantInPlace(std::vector<MontgomeryInt>* in1,
+                                               const Int& constant,
+                                               const Int& constant_barrett,
+                                               const Params* params);
+@@ -423,14 +425,14 @@ class ABSL_MUST_USE_RESULT MontgomeryInt {
+   bool operator!=(const MontgomeryInt& that) const { return !(*this == that); }
+ 
+   // Modular exponentiation.
+-  MontgomeryInt ModExp(Int exponent, const Params* params) const;
++  SHELL_ENCRYPTION_EXPORT MontgomeryInt ModExp(Int exponent, const Params* params) const;
+ 
+   // Inverse.
+-  MontgomeryInt MultiplicativeInverse(const Params* params) const;
++  SHELL_ENCRYPTION_EXPORT  MontgomeryInt MultiplicativeInverse(const Params* params) const;
+ 
+  private:
+   template <typename Prng = rlwe::SecurePrng>
+-  static rlwe::StatusOr<Int> GenerateRandomInt(int log_modulus, Prng* prng) {
++  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<Int> GenerateRandomInt(int log_modulus, Prng* prng) {
+     // Generate a random Int. As the modulus is always smaller than max(Int),
+     // there will be no issues with overflow.
+     int max_bits_per_step = std::min((int)Params::bitsize_int, (int)64);
+@@ -467,6 +469,17 @@ class ABSL_MUST_USE_RESULT MontgomeryInt {
+   Int n_;
+ };
+ 
++// Instantiations of MontgomeryInt and MontgomeryIntParams with specific
++// integral types.
++extern template struct EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) MontgomeryIntParams<Uint16>;
++extern template struct EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) MontgomeryIntParams<Uint32>;
++extern template struct EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) MontgomeryIntParams<Uint64>;
++extern template struct EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) MontgomeryIntParams<absl::uint128>;
++extern template class EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) MontgomeryInt<Uint16>;
++extern template class EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) MontgomeryInt<Uint32>;
++extern template class EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) MontgomeryInt<Uint64>;
++extern template class EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) MontgomeryInt<absl::uint128>;
++
+ }  // namespace rlwe
+ 
+ #endif  // RLWE_MONTGOMERY_H_
+diff --git a/montgomery_test.cc b/montgomery_test.cc
+index 7caf459a4c08b..2d952ddfbdad4 100644
+--- a/montgomery_test.cc
++++ b/montgomery_test.cc
+@@ -30,6 +30,8 @@
+ #include "testing/status_matchers.h"
+ #include "testing/status_testing.h"
+ #include "testing/testing_prng.h"
++#include "third_party/shell-encryption/base/shell_encryption_export.h"
++#include "third_party/shell-encryption/base/shell_encryption_export_template.h"
+ 
+ namespace rlwe {
+ namespace {
+@@ -65,7 +67,7 @@ uint256 GenerateRandom(unsigned int* seed) {
+ }
+ 
+ template <typename T>
+-class MontgomeryTest : public ::testing::Test {};
++class EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) MontgomeryTest : public ::testing::Test {};
+ TYPED_TEST_SUITE(MontgomeryTest, testing::ModularIntTypes);
+ 
+ TYPED_TEST(MontgomeryTest, ModulusTooLarge) {
+diff --git a/ntt_parameters.h b/ntt_parameters.h
+index 55671ec5e65de..eba9579ab87ca 100644
+--- a/ntt_parameters.h
++++ b/ntt_parameters.h
+@@ -24,6 +24,7 @@
+ #include "constants.h"
+ #include "status_macros.h"
+ #include "statusor.h"
++#include "third_party/shell-encryption/base/shell_encryption_export.h"
+ 
+ namespace rlwe {
+ namespace internal {
+@@ -94,7 +95,7 @@ rlwe::StatusOr<std::vector<ModularInt>> NttPsis(
+ // Creates a vector containing the indices necessary to perform the NTT bit
+ // reversal operation. Index i of the returned vector contains an integer with
+ // the rightmost log_n bits of i reversed.
+-std::vector<unsigned int> BitrevArray(unsigned int log_n);
++SHELL_ENCRYPTION_EXPORT std::vector<unsigned int> BitrevArray(unsigned int log_n);
+ 
+ // Helper function: Perform the bit-reversal operation in-place on coeffs_.
+ template <typename ModularInt>
+diff --git a/prng/chacha_prng.h b/prng/chacha_prng.h
+index 27940fd82c646..7e4f719fedaf6 100644
+--- a/prng/chacha_prng.h
++++ b/prng/chacha_prng.h
+@@ -26,6 +26,7 @@
+ #include "prng/chacha_prng_util.h"
+ #include "prng/prng.h"
+ #include "statusor.h"
++#include "third_party/shell-encryption/base/shell_encryption_export.h"
+ 
+ namespace rlwe {
+ 
+@@ -56,7 +57,7 @@ class ChaChaPrng : public SecurePrng {
+   // errors.
+   //
+   // Thread safe.
+-  static rlwe::StatusOr<std::unique_ptr<ChaChaPrng>> Create(
++  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::unique_ptr<ChaChaPrng>> Create(
+       absl::string_view in_key);
+ 
+   // Returns 8 bits of randomness.
+@@ -72,12 +73,12 @@ class ChaChaPrng : public SecurePrng {
+   // Generate a valid seed for the Prng.
+   //
+   // Fails on internal cryptographic errors.
+-  static rlwe::StatusOr<std::string> GenerateSeed() {
++  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::string> GenerateSeed() {
+     return internal::ChaChaPrngGenerateKey();
+   }
+ 
+   // Output the size of the expected generated seed.
+-  static int SeedLength() { return internal::kChaChaKeyBytesSize; }
++  static SHELL_ENCRYPTION_EXPORT int SeedLength() { return internal::kChaChaKeyBytesSize; }
+ 
+  private:
+   explicit ChaChaPrng(absl::string_view in_key, int position_in_buffer,
+diff --git a/prng/chacha_prng_util.h b/prng/chacha_prng_util.h
+index 8eb8118fe26f9..80f0cedf4a703 100644
+--- a/prng/chacha_prng_util.h
++++ b/prng/chacha_prng_util.h
+@@ -27,6 +27,7 @@
+ #include "absl/strings/string_view.h"
+ #include "integral_types.h"
+ #include "statusor.h"
++#include "third_party/shell-encryption/base/shell_encryption_export.h"
+ 
+ namespace rlwe {
+ namespace internal {
+@@ -37,28 +38,33 @@ const int kChaChaOutputBytes = 255 * 32;
+ 
+ // Once pseudorandom output is exhausted, the salt is updated to construct
+ // new pseudorandom output.
+-absl::Status ChaChaPrngResalt(absl::string_view key, int buffer_size,
+-                              int* salt_counter, int* position_in_buffer,
+-                              std::vector<Uint8>* buffer);
++SHELL_ENCRYPTION_EXPORT absl::Status ChaChaPrngResalt(
++    absl::string_view key,
++    int buffer_size,
++    int* salt_counter,
++    int* position_in_buffer,
++    std::vector<Uint8>* buffer);
+ 
+ // Generates a secure key for instantiating an CHACHA.
+-rlwe::StatusOr<std::string> ChaChaPrngGenerateKey();
++SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::string> ChaChaPrngGenerateKey();
+ 
+ // Returns 8 bits of randomness.
+ //
+ // Fails on internal cryptographic errors.
+-rlwe::StatusOr<Uint8> ChaChaPrngRand8(absl::string_view key,
+-                                      int* position_in_buffer,
+-                                      int* salt_counter,
+-                                      std::vector<Uint8>* buffer);
++SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<Uint8> ChaChaPrngRand8(
++    absl::string_view key,
++    int* position_in_buffer,
++    int* salt_counter,
++    std::vector<Uint8>* buffer);
+ 
+ // Returns 64 bits of randomness.
+ //
+ // Fails on internal cryptographic errors.
+-rlwe::StatusOr<Uint64> ChaChaPrngRand64(absl::string_view key,
+-                                        int* position_in_buffer,
+-                                        int* salt_counter,
+-                                        std::vector<Uint8>* buffer);
++SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<Uint64> ChaChaPrngRand64(
++    absl::string_view key,
++    int* position_in_buffer,
++    int* salt_counter,
++    std::vector<Uint8>* buffer);
+ 
+ }  // namespace internal
+ }  // namespace rlwe
+diff --git a/prng/single_thread_chacha_prng.h b/prng/single_thread_chacha_prng.h
+index fcaff827be355..2fddf6f3530c4 100644
+--- a/prng/single_thread_chacha_prng.h
++++ b/prng/single_thread_chacha_prng.h
+@@ -24,6 +24,7 @@
+ #include "prng/chacha_prng_util.h"
+ #include "prng/prng.h"
+ #include "statusor.h"
++#include "third_party/shell-encryption/base/shell_encryption_export.h"
+ 
+ namespace rlwe {
+ 
+@@ -52,7 +53,7 @@ class SingleThreadChaChaPrng : public SecurePrng {
+   //
+   // Fails if the key is not the expected size or on internal cryptographic
+   // errors.
+-  static rlwe::StatusOr<std::unique_ptr<SingleThreadChaChaPrng>> Create(
++  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::unique_ptr<SingleThreadChaChaPrng>> Create(
+       absl::string_view in_key);
+ 
+   // Returns 8 bits of randomness.
+diff --git a/relinearization_key.cc b/relinearization_key.cc
+index 7982a2f71a74c..0b22a50e0abb7 100644
+--- a/relinearization_key.cc
++++ b/relinearization_key.cc
+@@ -22,6 +22,8 @@
+ #include "status_macros.h"
+ #include "statusor.h"
+ #include "symmetric_encryption_with_prng.h"
++#include "third_party/shell-encryption/base/shell_encryption_export.h"
++#include "third_party/shell-encryption/base/shell_encryption_export_template.h"
+ 
+ namespace rlwe {
+ namespace {
+@@ -432,9 +434,9 @@ RelinearizationKey<ModularInt>::Deserialize(
+ // Instantiations of RelinearizationKey with specific MontgomeryInt classes.
+ // If any new types are added, montgomery.h should be updated accordingly (such
+ // as ensuring BigInt is correctly specialized, etc.).
+-template class RelinearizationKey<MontgomeryInt<Uint16>>;
+-template class RelinearizationKey<MontgomeryInt<Uint32>>;
+-template class RelinearizationKey<MontgomeryInt<Uint64>>;
+-template class RelinearizationKey<MontgomeryInt<absl::uint128>>;
++template class EXPORT_TEMPLATE_DEFINE(SHELL_ENCRYPTION_EXPORT) RelinearizationKey<MontgomeryInt<Uint16>>;
++template class EXPORT_TEMPLATE_DEFINE(SHELL_ENCRYPTION_EXPORT) RelinearizationKey<MontgomeryInt<Uint32>>;
++template class EXPORT_TEMPLATE_DEFINE(SHELL_ENCRYPTION_EXPORT) RelinearizationKey<MontgomeryInt<Uint64>>;
++template class EXPORT_TEMPLATE_DEFINE(SHELL_ENCRYPTION_EXPORT) RelinearizationKey<MontgomeryInt<absl::uint128>>;
+ 
+ }  //  namespace rlwe
+diff --git a/relinearization_key.h b/relinearization_key.h
+index 265da33e5af15..1aff179b9ed39 100644
+--- a/relinearization_key.h
++++ b/relinearization_key.h
+@@ -22,6 +22,8 @@
+ #include "sample_error.h"
+ #include "statusor.h"
+ #include "symmetric_encryption.h"
++#include "third_party/shell-encryption/base/shell_encryption_export.h"
++#include "third_party/shell-encryption/base/shell_encryption_export_template.h"
+ 
+ namespace rlwe {
+ // Represents a RelinearizationKey constructed from a symmetric-key. Applying a
+@@ -57,7 +59,7 @@ namespace rlwe {
+ // length (k - 1), where k is the number of parts of the ciphertext it applies
+ // to.
+ template <typename ModularInt>
+-class RelinearizationKey {
++class EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) RelinearizationKey {
+   using ModularIntParams = typename ModularInt::Params;
+ 
+  public:
+@@ -73,7 +75,7 @@ class RelinearizationKey {
+   // with (1, s^k). In that case, we would use a relinearization key with
+   // substition_power = k to return the ciphertext to be encrypted with (1,s).
+   // See GaloisKey for an explicit wrapper around RelinearizationKey.
+-  static rlwe::StatusOr<RelinearizationKey> Create(
++  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<RelinearizationKey> Create(
+       const SymmetricRlweKey<ModularInt>& key, absl::string_view prng_seed,
+       ssize_t num_parts, Uint64 log_decomposition_modulus,
+       Uint64 substitution_power = 1);
+@@ -192,6 +194,13 @@ class RelinearizationKey {
+   std::string prng_seed_;
+ };
+ 
++// Instantiations of RelinearizationKey with specific MontgomeryInt classes.
++// If any new types are added, montgomery.h should be updated accordingly (such
++// as ensuring BigInt is correctly specialized, etc.).
++extern template class EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) RelinearizationKey<MontgomeryInt<Uint16>>;
++extern template class EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) RelinearizationKey<MontgomeryInt<Uint32>>;
++extern template class EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) RelinearizationKey<MontgomeryInt<Uint64>>;
++extern template class EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) RelinearizationKey<MontgomeryInt<absl::uint128>>;
+ }  // namespace rlwe
+ 
+ #endif  // RLWE_RELINEARIZATION_KEY_H_
+diff --git a/statusor.h b/statusor.h
+index 200f62d917d0f..b7ada09372c9f 100644
+--- a/statusor.h
++++ b/statusor.h
+@@ -22,11 +22,12 @@
+ #include "absl/base/attributes.h"
+ #include "absl/status/status.h"
+ #include "absl/types/optional.h"
++#include "third_party/shell-encryption/base/shell_encryption_export.h"
+ 
+ namespace rlwe {
+ 
+ template <typename T>
+-class StatusOr {
++class SHELL_ENCRYPTION_EXPORT StatusOr {
+  public:
+   // Construct a new StatusOr with Status::UNKNOWN status
+   StatusOr();
+@@ -112,12 +113,12 @@ class StatusOr {
+ 
+ namespace internal {
+ 
+-class StatusOrHelper {
++class SHELL_ENCRYPTION_EXPORT StatusOrHelper {
+  public:
+   // Move type-agnostic error handling to the .cc.
+-  static absl::Status HandleInvalidStatusCtorArg();
+-  static absl::Status HandleNullObjectCtorArg();
+-  static void Crash(const absl::Status& status);
++  static SHELL_ENCRYPTION_EXPORT absl::Status HandleInvalidStatusCtorArg();
++  static SHELL_ENCRYPTION_EXPORT absl::Status HandleNullObjectCtorArg();
++  static SHELL_ENCRYPTION_EXPORT void Crash(const absl::Status& status);
+ 
+   // Customized behavior for StatusOr<T> vs. StatusOr<T*>
+   template <typename T>
+@@ -125,13 +126,13 @@ class StatusOrHelper {
+ };
+ 
+ template <typename T>
+-struct StatusOrHelper::Specialize {
++struct SHELL_ENCRYPTION_EXPORT StatusOrHelper::Specialize {
+   // For non-pointer T, a reference can never be NULL.
+   static inline bool IsValueNull(const T& t) { return false; }
+ };
+ 
+ template <typename T>
+-struct StatusOrHelper::Specialize<T*> {
++struct SHELL_ENCRYPTION_EXPORT StatusOrHelper::Specialize<T*> {
+   static inline bool IsValueNull(const T* t) { return t == nullptr; }
+ };
+ 
diff --git a/src/galois_key.h b/src/galois_key.h
index 0b73a63..e6295f1 100644
--- a/src/galois_key.h
+++ b/src/galois_key.h
@@ -23,6 +23,8 @@
 #include "relinearization_key.h"
 #include "status_macros.h"
 #include "statusor.h"
+#include "third_party/shell-encryption/base/shell_encryption_export.h"
+#include "third_party/shell-encryption/base/shell_encryption_export_template.h"
 
 namespace rlwe {
 
@@ -41,7 +43,7 @@
 //
 // Details can be found in Appendix D.2 of https://eprint.iacr.org/2011/566.pdf
 template <typename ModularInt>
-class GaloisKey {
+class EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) GaloisKey {
  public:
   // Initializes a GaloisKey based on a SymmetricRlweKey key that can key-switch
   // two component ciphertexts. A positive log_decomposition_modulus corresponds
@@ -49,7 +51,7 @@
   // power of x in the secret key polynomial s(x^substitution_power) that the
   // ciphertext is encrypted with. The prng_seed is used to generate and encode
   // the bottom row of the matrix, which consists of random entries.
-  static rlwe::StatusOr<GaloisKey> Create(
+  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<GaloisKey> Create(
       const SymmetricRlweKey<ModularInt>& key, absl::string_view prng_seed,
       Uint64 substitution_power, Uint64 log_decomposition_modulus) {
     RLWE_ASSIGN_OR_RETURN(auto relinearization_key,
@@ -88,7 +90,7 @@
   // SerializedGaloisKey is (2 * num_parts * dimension) where dimension is the
   // number of digits needed to represent the modulus in base
   // 2^{log_decomposition_modulus}. Crashes for non-valid input parameters.
-  static rlwe::StatusOr<GaloisKey> Deserialize(
+  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<GaloisKey> Deserialize(
       const SerializedGaloisKey& serialized,
       const typename ModularInt::Params* modulus_params,
       const NttParameters<ModularInt>* ntt_params) {
@@ -110,6 +112,14 @@
   RelinearizationKey<ModularInt> relinearization_key_;
 };
 
+template class EXPORT_TEMPLATE_DECLARE(
+    SHELL_ENCRYPTION_EXPORT) GaloisKey<rlwe::MontgomeryInt<Uint16>>;
+template class EXPORT_TEMPLATE_DECLARE(
+    SHELL_ENCRYPTION_EXPORT) GaloisKey<rlwe::MontgomeryInt<Uint32>>;
+template class EXPORT_TEMPLATE_DECLARE(
+    SHELL_ENCRYPTION_EXPORT) GaloisKey<rlwe::MontgomeryInt<Uint64>>;
+template class EXPORT_TEMPLATE_DECLARE(
+    SHELL_ENCRYPTION_EXPORT) GaloisKey<rlwe::MontgomeryInt<absl::uint128>>;
 }  //  namespace rlwe
 
 #endif  // RLWE_GALOIS_KEY_H_
diff --git a/src/int256.h b/src/int256.h
index 540dce2..4c464a4 100644
--- a/src/int256.h
+++ b/src/int256.h
@@ -18,13 +18,15 @@
 
 #include "absl/numeric/int128.h"
 #include "integral_types.h"
+#include "third_party/shell-encryption/base/shell_encryption_export.h"
+#include "third_party/shell-encryption/base/shell_encryption_export_template.h"
 
 namespace rlwe {
 
 struct uint256_pod;
 
 // An unsigned 256-bit integer type. Thread-compatible.
-class uint256 {
+class SHELL_ENCRYPTION_EXPORT uint256 {
  public:
   constexpr uint256();
   constexpr uint256(absl::uint128 top, absl::uint128 bottom);
@@ -74,11 +76,11 @@
   uint256& operator-=(const uint256& b);
   uint256& operator*=(const uint256& b);
   // Long division/modulo for uint256.
-  uint256& operator/=(const uint256& b);
-  uint256& operator%=(const uint256& b);
+  SHELL_ENCRYPTION_EXPORT uint256& operator/=(const uint256& b);
+  SHELL_ENCRYPTION_EXPORT uint256& operator%=(const uint256& b);
   uint256 operator++(int);
   uint256 operator--(int);
-  uint256& operator<<=(int);
+  SHELL_ENCRYPTION_EXPORT uint256& operator<<=(int);
   uint256& operator>>=(int);
   uint256& operator&=(const uint256& b);
   uint256& operator|=(const uint256& b);
@@ -90,7 +92,7 @@
   friend absl::uint128 Uint256High128(const uint256& v);
 
   // We add "std::" to avoid including all of port.h.
-  friend std::ostream& operator<<(std::ostream& o, const uint256& b);
+  friend SHELL_ENCRYPTION_EXPORT std::ostream& operator<<(std::ostream& o, const uint256& b);
 
  private:
   static void DivModImpl(uint256 dividend, uint256 divisor,
@@ -121,7 +123,7 @@
 
 // This is a POD form of uint256 which can be used for static variables which
 // need to be operated on as uint256.
-struct uint256_pod {
+struct SHELL_ENCRYPTION_EXPORT uint256_pod {
   // Note: The ordering of fields is different than 'class uint256' but the
   // same as its 2-arg constructor.  This enables more obvious initialization
   // of static instances, which is the primary reason for this struct in the
diff --git a/src/montgomery.cc b/src/montgomery.cc
index 9fbc5da..4bd0336 100644
--- a/src/montgomery.cc
+++ b/src/montgomery.cc
@@ -14,6 +14,8 @@
 
 #include "montgomery.h"
 
+#include "third_party/shell-encryption/base/shell_encryption_export.h"
+#include "third_party/shell-encryption/base/shell_encryption_export_template.h"
 #include "transcription.h"
 
 namespace rlwe {
@@ -416,12 +418,12 @@
 
 // Instantiations of MontgomeryInt and MontgomeryIntParams with specific
 // integral types.
-template struct MontgomeryIntParams<Uint16>;
-template struct MontgomeryIntParams<Uint32>;
-template struct MontgomeryIntParams<Uint64>;
-template struct MontgomeryIntParams<absl::uint128>;
-template class MontgomeryInt<Uint16>;
-template class MontgomeryInt<Uint32>;
-template class MontgomeryInt<Uint64>;
-template class MontgomeryInt<absl::uint128>;
+template struct EXPORT_TEMPLATE_DEFINE(SHELL_ENCRYPTION_EXPORT) MontgomeryIntParams<Uint16>;
+template struct EXPORT_TEMPLATE_DEFINE(SHELL_ENCRYPTION_EXPORT) MontgomeryIntParams<Uint32>;
+template struct EXPORT_TEMPLATE_DEFINE(SHELL_ENCRYPTION_EXPORT) MontgomeryIntParams<Uint64>;
+template struct EXPORT_TEMPLATE_DEFINE(SHELL_ENCRYPTION_EXPORT) MontgomeryIntParams<absl::uint128>;
+template class EXPORT_TEMPLATE_DEFINE(SHELL_ENCRYPTION_EXPORT) MontgomeryInt<Uint16>;
+template class EXPORT_TEMPLATE_DEFINE(SHELL_ENCRYPTION_EXPORT) MontgomeryInt<Uint32>;
+template class EXPORT_TEMPLATE_DEFINE(SHELL_ENCRYPTION_EXPORT) MontgomeryInt<Uint64>;
+template class EXPORT_TEMPLATE_DEFINE(SHELL_ENCRYPTION_EXPORT) MontgomeryInt<absl::uint128>;
 }  // namespace rlwe
diff --git a/src/montgomery.h b/src/montgomery.h
index 4f0e2ea..c3988ff 100644
--- a/src/montgomery.h
+++ b/src/montgomery.h
@@ -34,6 +34,8 @@
 #include "serialization.pb.h"
 #include "status_macros.h"
 #include "statusor.h"
+#include "third_party/shell-encryption/base/shell_encryption_export.h"
+#include "third_party/shell-encryption/base/shell_encryption_export_template.h"
 
 namespace rlwe {
 
@@ -43,24 +45,24 @@
 template <typename T>
 struct BigInt;
 // Specialization for uint8, uint16, uint32, uint64, and uint128.
-template <>
-struct BigInt<Uint8> {
+template <> 
+struct EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) BigInt<Uint8> {
   typedef Uint16 value_type;
 };
-template <>
-struct BigInt<Uint16> {
+template <> 
+struct EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) BigInt<Uint16> {
   typedef Uint32 value_type;
 };
-template <>
-struct BigInt<Uint32> {
+template <> 
+struct EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) BigInt<Uint32> {
   typedef Uint64 value_type;
 };
-template <>
-struct BigInt<Uint64> {
+template <> 
+struct EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) BigInt<Uint64> {
   typedef absl::uint128 value_type;
 };
-template <>
-struct BigInt<absl::uint128> {
+template <> 
+struct EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) BigInt<absl::uint128> {
   typedef uint256 value_type;
 };
 
@@ -69,7 +71,7 @@
 // The parameters necessary for a Montgomery integer. Note that the template
 // parameters ensure that T is an unsigned integral of at least 8 bits.
 template <typename T>
-struct MontgomeryIntParams {
+struct EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) MontgomeryIntParams{
   // Expose Int and its greater type. BigInt is required in order to multiply
   // two Int and ensure that no overflow occurs.
   //
@@ -159,7 +161,7 @@
   // modulus must be odd.
   // Returns a tuple of (inv_r, inv_modulus) such that:
   //     r * inv_r - modulus * inv_modulus = 1
-  static std::tuple<Int, Int> Inverses(BigInt modulus_bigint, BigInt r);
+  static SHELL_ENCRYPTION_EXPORT std::tuple<Int, Int> Inverses(BigInt modulus_bigint, BigInt r);
 };
 
 // Stores an integer in Montgomery representation. The goal of this
@@ -170,7 +172,7 @@
 // The underlying integer type T must be unsigned and must not be bool.
 // This class is thread safe.
 template <typename T>
-class ABSL_MUST_USE_RESULT MontgomeryInt {
+class EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) ABSL_MUST_USE_RESULT MontgomeryInt {
  public:
   // Expose Int and its greater type. BigInt is required in order to multiply
   // two Int and ensure that no overflow occurs. This should also be used by
@@ -184,16 +186,16 @@
   // Static factory that converts a non-Montgomery representation integer, the
   // underlying integer type, into a Montgomery representation integer. Does not
   // take ownership of params. i.e., import "a".
-  static rlwe::StatusOr<MontgomeryInt> ImportInt(Int n, const Params* params);
+  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<MontgomeryInt> ImportInt(Int n, const Params* params);
 
   // Static functions to create a MontgomeryInt of 0 and 1.
-  static MontgomeryInt ImportZero(const Params* params);
-  static MontgomeryInt ImportOne(const Params* params);
+  static SHELL_ENCRYPTION_EXPORT MontgomeryInt ImportZero(const Params* params);
+  static SHELL_ENCRYPTION_EXPORT MontgomeryInt ImportOne(const Params* params);
 
   // Import a random integer using entropy from specified prng. Does not take
   // ownership of params or prng.
   template <typename Prng = rlwe::SecurePrng>
-  static rlwe::StatusOr<MontgomeryInt> ImportRandom(Prng* prng,
+  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<MontgomeryInt> ImportRandom(Prng* prng,
                                                     const Params* params) {
     // In order to generate unbiased randomness, we uniformly and randomly
     // sample integers in [0, 2^params->log_modulus) until the generated integer
@@ -234,13 +236,13 @@
 
   // Serialization.
   rlwe::StatusOr<std::string> Serialize(const Params* params) const;
-  static rlwe::StatusOr<std::string> SerializeVector(
+  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::string> SerializeVector(
       const std::vector<MontgomeryInt>& coeffs, const Params* params);
 
   // Deserialization.
-  static rlwe::StatusOr<MontgomeryInt> Deserialize(absl::string_view payload,
+  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<MontgomeryInt> Deserialize(absl::string_view payload,
                                                    const Params* params);
-  static rlwe::StatusOr<std::vector<MontgomeryInt>> DeserializeVector(
+  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::vector<MontgomeryInt>> DeserializeVector(
       int num_coeffs, absl::string_view serialized, const Params* params);
 
   // Modular multiplication.
@@ -353,7 +355,7 @@
   // size.
 
   // Batch addition of two vectors.
-  static rlwe::StatusOr<std::vector<MontgomeryInt>> BatchAdd(
+  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::vector<MontgomeryInt>> BatchAdd(
       const std::vector<MontgomeryInt>& in1,
       const std::vector<MontgomeryInt>& in2, const Params* params);
   static absl::Status BatchAddInPlace(std::vector<MontgomeryInt>* in1,
@@ -361,7 +363,7 @@
                                       const Params* params);
 
   // Batch addition of one vector with a scalar.
-  static rlwe::StatusOr<std::vector<MontgomeryInt>> BatchAdd(
+  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::vector<MontgomeryInt>> BatchAdd(
       const std::vector<MontgomeryInt>& in1, const MontgomeryInt& in2,
       const Params* params);
   static absl::Status BatchAddInPlace(std::vector<MontgomeryInt>* in1,
@@ -369,51 +371,51 @@
                                       const Params* params);
 
   // Batch subtraction of two vectors.
-  static rlwe::StatusOr<std::vector<MontgomeryInt>> BatchSub(
+  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::vector<MontgomeryInt>> BatchSub(
       const std::vector<MontgomeryInt>& in1,
       const std::vector<MontgomeryInt>& in2, const Params* params);
-  static absl::Status BatchSubInPlace(std::vector<MontgomeryInt>* in1,
+  static SHELL_ENCRYPTION_EXPORT absl::Status BatchSubInPlace(std::vector<MontgomeryInt>* in1,
                                       const std::vector<MontgomeryInt>& in2,
                                       const Params* params);
 
   // Batch subtraction of one vector with a scalar.
-  static rlwe::StatusOr<std::vector<MontgomeryInt>> BatchSub(
+  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::vector<MontgomeryInt>> BatchSub(
       const std::vector<MontgomeryInt>& in1, const MontgomeryInt& in2,
       const Params* params);
-  static absl::Status BatchSubInPlace(std::vector<MontgomeryInt>* in1,
+  static SHELL_ENCRYPTION_EXPORT absl::Status BatchSubInPlace(std::vector<MontgomeryInt>* in1,
                                       const MontgomeryInt& in2,
                                       const Params* params);
 
   // Batch multiplication of two vectors.
-  static rlwe::StatusOr<std::vector<MontgomeryInt>> BatchMul(
+  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::vector<MontgomeryInt>> BatchMul(
       const std::vector<MontgomeryInt>& in1,
       const std::vector<MontgomeryInt>& in2, const Params* params);
-  static absl::Status BatchMulInPlace(std::vector<MontgomeryInt>* in1,
+  static SHELL_ENCRYPTION_EXPORT absl::Status BatchMulInPlace(std::vector<MontgomeryInt>* in1,
                                       const std::vector<MontgomeryInt>& in2,
                                       const Params* params);
 
   // Batch multiplication of two vectors, where the second vector is a constant.
-  static rlwe::StatusOr<std::vector<MontgomeryInt>> BatchMulConstant(
+  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::vector<MontgomeryInt>> BatchMulConstant(
       const std::vector<MontgomeryInt>& in1,
       const std::vector<Int>& in2_constant,
       const std::vector<Int>& in2_constant_barrett, const Params* params);
-  static absl::Status BatchMulConstantInPlace(
+  static SHELL_ENCRYPTION_EXPORT absl::Status BatchMulConstantInPlace(
       std::vector<MontgomeryInt>* in1, const std::vector<Int>& in2_constant,
       const std::vector<Int>& in2_constant_barrett, const Params* params);
 
   // Batch multiplication of a vector with a scalar.
-  static rlwe::StatusOr<std::vector<MontgomeryInt>> BatchMul(
+  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::vector<MontgomeryInt>> BatchMul(
       const std::vector<MontgomeryInt>& in1, const MontgomeryInt& in2,
       const Params* params);
-  static absl::Status BatchMulInPlace(std::vector<MontgomeryInt>* in1,
+  static SHELL_ENCRYPTION_EXPORT absl::Status BatchMulInPlace(std::vector<MontgomeryInt>* in1,
                                       const MontgomeryInt& in2,
                                       const Params* params);
 
   // Batch multiplication of a vector with a constant scalar.
-  static rlwe::StatusOr<std::vector<MontgomeryInt>> BatchMulConstant(
+  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::vector<MontgomeryInt>> BatchMulConstant(
       const std::vector<MontgomeryInt>& in1, const Int& constant,
       const Int& constant_barrett, const Params* params);
-  static absl::Status BatchMulConstantInPlace(std::vector<MontgomeryInt>* in1,
+  static SHELL_ENCRYPTION_EXPORT absl::Status BatchMulConstantInPlace(std::vector<MontgomeryInt>* in1,
                                               const Int& constant,
                                               const Int& constant_barrett,
                                               const Params* params);
@@ -423,14 +425,14 @@
   bool operator!=(const MontgomeryInt& that) const { return !(*this == that); }
 
   // Modular exponentiation.
-  MontgomeryInt ModExp(Int exponent, const Params* params) const;
+  SHELL_ENCRYPTION_EXPORT MontgomeryInt ModExp(Int exponent, const Params* params) const;
 
   // Inverse.
-  MontgomeryInt MultiplicativeInverse(const Params* params) const;
+  SHELL_ENCRYPTION_EXPORT  MontgomeryInt MultiplicativeInverse(const Params* params) const;
 
  private:
   template <typename Prng = rlwe::SecurePrng>
-  static rlwe::StatusOr<Int> GenerateRandomInt(int log_modulus, Prng* prng) {
+  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<Int> GenerateRandomInt(int log_modulus, Prng* prng) {
     // Generate a random Int. As the modulus is always smaller than max(Int),
     // there will be no issues with overflow.
     int max_bits_per_step = std::min((int)Params::bitsize_int, (int)64);
@@ -467,6 +469,17 @@
   Int n_;
 };
 
+// Instantiations of MontgomeryInt and MontgomeryIntParams with specific
+// integral types.
+extern template struct EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) MontgomeryIntParams<Uint16>;
+extern template struct EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) MontgomeryIntParams<Uint32>;
+extern template struct EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) MontgomeryIntParams<Uint64>;
+extern template struct EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) MontgomeryIntParams<absl::uint128>;
+extern template class EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) MontgomeryInt<Uint16>;
+extern template class EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) MontgomeryInt<Uint32>;
+extern template class EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) MontgomeryInt<Uint64>;
+extern template class EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) MontgomeryInt<absl::uint128>;
+
 }  // namespace rlwe
 
 #endif  // RLWE_MONTGOMERY_H_
diff --git a/src/montgomery_test.cc b/src/montgomery_test.cc
index 7caf459..2d952dd 100644
--- a/src/montgomery_test.cc
+++ b/src/montgomery_test.cc
@@ -30,6 +30,8 @@
 #include "testing/status_matchers.h"
 #include "testing/status_testing.h"
 #include "testing/testing_prng.h"
+#include "third_party/shell-encryption/base/shell_encryption_export.h"
+#include "third_party/shell-encryption/base/shell_encryption_export_template.h"
 
 namespace rlwe {
 namespace {
@@ -65,7 +67,7 @@
 }
 
 template <typename T>
-class MontgomeryTest : public ::testing::Test {};
+class EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) MontgomeryTest : public ::testing::Test {};
 TYPED_TEST_SUITE(MontgomeryTest, testing::ModularIntTypes);
 
 TYPED_TEST(MontgomeryTest, ModulusTooLarge) {
diff --git a/src/ntt_parameters.h b/src/ntt_parameters.h
index 55671ec..eba9579 100644
--- a/src/ntt_parameters.h
+++ b/src/ntt_parameters.h
@@ -24,6 +24,7 @@
 #include "constants.h"
 #include "status_macros.h"
 #include "statusor.h"
+#include "third_party/shell-encryption/base/shell_encryption_export.h"
 
 namespace rlwe {
 namespace internal {
@@ -94,7 +95,7 @@
 // Creates a vector containing the indices necessary to perform the NTT bit
 // reversal operation. Index i of the returned vector contains an integer with
 // the rightmost log_n bits of i reversed.
-std::vector<unsigned int> BitrevArray(unsigned int log_n);
+SHELL_ENCRYPTION_EXPORT std::vector<unsigned int> BitrevArray(unsigned int log_n);
 
 // Helper function: Perform the bit-reversal operation in-place on coeffs_.
 template <typename ModularInt>
diff --git a/src/prng/chacha_prng.h b/src/prng/chacha_prng.h
index 27940fd..7e4f719 100644
--- a/src/prng/chacha_prng.h
+++ b/src/prng/chacha_prng.h
@@ -26,6 +26,7 @@
 #include "prng/chacha_prng_util.h"
 #include "prng/prng.h"
 #include "statusor.h"
+#include "third_party/shell-encryption/base/shell_encryption_export.h"
 
 namespace rlwe {
 
@@ -56,7 +57,7 @@
   // errors.
   //
   // Thread safe.
-  static rlwe::StatusOr<std::unique_ptr<ChaChaPrng>> Create(
+  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::unique_ptr<ChaChaPrng>> Create(
       absl::string_view in_key);
 
   // Returns 8 bits of randomness.
@@ -72,12 +73,12 @@
   // Generate a valid seed for the Prng.
   //
   // Fails on internal cryptographic errors.
-  static rlwe::StatusOr<std::string> GenerateSeed() {
+  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::string> GenerateSeed() {
     return internal::ChaChaPrngGenerateKey();
   }
 
   // Output the size of the expected generated seed.
-  static int SeedLength() { return internal::kChaChaKeyBytesSize; }
+  static SHELL_ENCRYPTION_EXPORT int SeedLength() { return internal::kChaChaKeyBytesSize; }
 
  private:
   explicit ChaChaPrng(absl::string_view in_key, int position_in_buffer,
diff --git a/src/prng/chacha_prng_util.h b/src/prng/chacha_prng_util.h
index 8eb8118..80f0ced 100644
--- a/src/prng/chacha_prng_util.h
+++ b/src/prng/chacha_prng_util.h
@@ -27,6 +27,7 @@
 #include "absl/strings/string_view.h"
 #include "integral_types.h"
 #include "statusor.h"
+#include "third_party/shell-encryption/base/shell_encryption_export.h"
 
 namespace rlwe {
 namespace internal {
@@ -37,28 +38,33 @@
 
 // Once pseudorandom output is exhausted, the salt is updated to construct
 // new pseudorandom output.
-absl::Status ChaChaPrngResalt(absl::string_view key, int buffer_size,
-                              int* salt_counter, int* position_in_buffer,
-                              std::vector<Uint8>* buffer);
+SHELL_ENCRYPTION_EXPORT absl::Status ChaChaPrngResalt(
+    absl::string_view key,
+    int buffer_size,
+    int* salt_counter,
+    int* position_in_buffer,
+    std::vector<Uint8>* buffer);
 
 // Generates a secure key for instantiating an CHACHA.
-rlwe::StatusOr<std::string> ChaChaPrngGenerateKey();
+SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::string> ChaChaPrngGenerateKey();
 
 // Returns 8 bits of randomness.
 //
 // Fails on internal cryptographic errors.
-rlwe::StatusOr<Uint8> ChaChaPrngRand8(absl::string_view key,
-                                      int* position_in_buffer,
-                                      int* salt_counter,
-                                      std::vector<Uint8>* buffer);
+SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<Uint8> ChaChaPrngRand8(
+    absl::string_view key,
+    int* position_in_buffer,
+    int* salt_counter,
+    std::vector<Uint8>* buffer);
 
 // Returns 64 bits of randomness.
 //
 // Fails on internal cryptographic errors.
-rlwe::StatusOr<Uint64> ChaChaPrngRand64(absl::string_view key,
-                                        int* position_in_buffer,
-                                        int* salt_counter,
-                                        std::vector<Uint8>* buffer);
+SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<Uint64> ChaChaPrngRand64(
+    absl::string_view key,
+    int* position_in_buffer,
+    int* salt_counter,
+    std::vector<Uint8>* buffer);
 
 }  // namespace internal
 }  // namespace rlwe
diff --git a/src/prng/single_thread_chacha_prng.h b/src/prng/single_thread_chacha_prng.h
index fcaff82..2fddf6f 100644
--- a/src/prng/single_thread_chacha_prng.h
+++ b/src/prng/single_thread_chacha_prng.h
@@ -24,6 +24,7 @@
 #include "prng/chacha_prng_util.h"
 #include "prng/prng.h"
 #include "statusor.h"
+#include "third_party/shell-encryption/base/shell_encryption_export.h"
 
 namespace rlwe {
 
@@ -52,7 +53,7 @@
   //
   // Fails if the key is not the expected size or on internal cryptographic
   // errors.
-  static rlwe::StatusOr<std::unique_ptr<SingleThreadChaChaPrng>> Create(
+  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<std::unique_ptr<SingleThreadChaChaPrng>> Create(
       absl::string_view in_key);
 
   // Returns 8 bits of randomness.
diff --git a/src/relinearization_key.cc b/src/relinearization_key.cc
index 7982a2f..0b22a50 100644
--- a/src/relinearization_key.cc
+++ b/src/relinearization_key.cc
@@ -22,6 +22,8 @@
 #include "status_macros.h"
 #include "statusor.h"
 #include "symmetric_encryption_with_prng.h"
+#include "third_party/shell-encryption/base/shell_encryption_export.h"
+#include "third_party/shell-encryption/base/shell_encryption_export_template.h"
 
 namespace rlwe {
 namespace {
@@ -432,9 +434,9 @@
 // Instantiations of RelinearizationKey with specific MontgomeryInt classes.
 // If any new types are added, montgomery.h should be updated accordingly (such
 // as ensuring BigInt is correctly specialized, etc.).
-template class RelinearizationKey<MontgomeryInt<Uint16>>;
-template class RelinearizationKey<MontgomeryInt<Uint32>>;
-template class RelinearizationKey<MontgomeryInt<Uint64>>;
-template class RelinearizationKey<MontgomeryInt<absl::uint128>>;
+template class EXPORT_TEMPLATE_DEFINE(SHELL_ENCRYPTION_EXPORT) RelinearizationKey<MontgomeryInt<Uint16>>;
+template class EXPORT_TEMPLATE_DEFINE(SHELL_ENCRYPTION_EXPORT) RelinearizationKey<MontgomeryInt<Uint32>>;
+template class EXPORT_TEMPLATE_DEFINE(SHELL_ENCRYPTION_EXPORT) RelinearizationKey<MontgomeryInt<Uint64>>;
+template class EXPORT_TEMPLATE_DEFINE(SHELL_ENCRYPTION_EXPORT) RelinearizationKey<MontgomeryInt<absl::uint128>>;
 
 }  //  namespace rlwe
diff --git a/src/relinearization_key.h b/src/relinearization_key.h
index 265da33..1aff179 100644
--- a/src/relinearization_key.h
+++ b/src/relinearization_key.h
@@ -22,6 +22,8 @@
 #include "sample_error.h"
 #include "statusor.h"
 #include "symmetric_encryption.h"
+#include "third_party/shell-encryption/base/shell_encryption_export.h"
+#include "third_party/shell-encryption/base/shell_encryption_export_template.h"
 
 namespace rlwe {
 // Represents a RelinearizationKey constructed from a symmetric-key. Applying a
@@ -57,7 +59,7 @@
 // length (k - 1), where k is the number of parts of the ciphertext it applies
 // to.
 template <typename ModularInt>
-class RelinearizationKey {
+class EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) RelinearizationKey {
   using ModularIntParams = typename ModularInt::Params;
 
  public:
@@ -73,7 +75,7 @@
   // with (1, s^k). In that case, we would use a relinearization key with
   // substition_power = k to return the ciphertext to be encrypted with (1,s).
   // See GaloisKey for an explicit wrapper around RelinearizationKey.
-  static rlwe::StatusOr<RelinearizationKey> Create(
+  static SHELL_ENCRYPTION_EXPORT rlwe::StatusOr<RelinearizationKey> Create(
       const SymmetricRlweKey<ModularInt>& key, absl::string_view prng_seed,
       ssize_t num_parts, Uint64 log_decomposition_modulus,
       Uint64 substitution_power = 1);
@@ -192,6 +194,13 @@
   std::string prng_seed_;
 };
 
+// Instantiations of RelinearizationKey with specific MontgomeryInt classes.
+// If any new types are added, montgomery.h should be updated accordingly (such
+// as ensuring BigInt is correctly specialized, etc.).
+extern template class EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) RelinearizationKey<MontgomeryInt<Uint16>>;
+extern template class EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) RelinearizationKey<MontgomeryInt<Uint32>>;
+extern template class EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) RelinearizationKey<MontgomeryInt<Uint64>>;
+extern template class EXPORT_TEMPLATE_DECLARE(SHELL_ENCRYPTION_EXPORT) RelinearizationKey<MontgomeryInt<absl::uint128>>;
 }  // namespace rlwe
 
 #endif  // RLWE_RELINEARIZATION_KEY_H_
diff --git a/src/statusor.h b/src/statusor.h
index 200f62d..b7ada09 100644
--- a/src/statusor.h
+++ b/src/statusor.h
@@ -22,11 +22,12 @@
 #include "absl/base/attributes.h"
 #include "absl/status/status.h"
 #include "absl/types/optional.h"
+#include "third_party/shell-encryption/base/shell_encryption_export.h"
 
 namespace rlwe {
 
 template <typename T>
-class StatusOr {
+class SHELL_ENCRYPTION_EXPORT StatusOr {
  public:
   // Construct a new StatusOr with Status::UNKNOWN status
   StatusOr();
@@ -112,12 +113,12 @@
 
 namespace internal {
 
-class StatusOrHelper {
+class SHELL_ENCRYPTION_EXPORT StatusOrHelper {
  public:
   // Move type-agnostic error handling to the .cc.
-  static absl::Status HandleInvalidStatusCtorArg();
-  static absl::Status HandleNullObjectCtorArg();
-  static void Crash(const absl::Status& status);
+  static SHELL_ENCRYPTION_EXPORT absl::Status HandleInvalidStatusCtorArg();
+  static SHELL_ENCRYPTION_EXPORT absl::Status HandleNullObjectCtorArg();
+  static SHELL_ENCRYPTION_EXPORT void Crash(const absl::Status& status);
 
   // Customized behavior for StatusOr<T> vs. StatusOr<T*>
   template <typename T>
@@ -125,13 +126,13 @@
 };
 
 template <typename T>
-struct StatusOrHelper::Specialize {
+struct SHELL_ENCRYPTION_EXPORT StatusOrHelper::Specialize {
   // For non-pointer T, a reference can never be NULL.
   static inline bool IsValueNull(const T& t) { return false; }
 };
 
 template <typename T>
-struct StatusOrHelper::Specialize<T*> {
+struct SHELL_ENCRYPTION_EXPORT StatusOrHelper::Specialize<T*> {
   static inline bool IsValueNull(const T* t) { return t == nullptr; }
 };