Repository: hyperledger/quilt
Pull request #314 (2019-10-01 18:51:00)

One shared secret to rule them all
Refactor the 2 SharedSecret classes into 1 in ilp-core

theotherian: :shipit: 

Patch:
diff --git a/examples/send-money/src/main/java/org/interledger/examples/SendMoneyExample.java b/examples/send-money/src/main/java/org/interledger/examples/SendMoneyExample.java
index 270261e..dbe3c1b 100644
--- a/examples/send-money/src/main/java/org/interledger/examples/SendMoneyExample.java
+++ b/examples/send-money/src/main/java/org/interledger/examples/SendMoneyExample.java
@@ -13,7 +13,7 @@ import org.interledger.spsp.PaymentPointer;
 import org.interledger.spsp.StreamConnectionDetails;
 import org.interledger.spsp.client.rust.InterledgerRustNodeClient;
 import org.interledger.stream.SendMoneyResult;
-import org.interledger.stream.crypto.SharedSecret;
+import org.interledger.core.SharedSecret;
 import org.interledger.stream.sender.SimpleStreamSender;
 import java.util.Arrays;
 import java.util.concurrent.ExecutionException;
diff --git a/stream-parent/stream-core/src/main/java/org/interledger/stream/crypto/SharedSecret.java b/ilp-core/src/main/java/org/interledger/core/SharedSecret.java
similarity index 89%
rename from stream-parent/stream-core/src/main/java/org/interledger/stream/crypto/SharedSecret.java
rename to ilp-core/src/main/java/org/interledger/core/SharedSecret.java
index 6121724..14ebfa0 100644
--- a/stream-parent/stream-core/src/main/java/org/interledger/stream/crypto/SharedSecret.java
+++ b/ilp-core/src/main/java/org/interledger/core/SharedSecret.java
@@ -1,6 +1,5 @@
-package org.interledger.stream.crypto;
+package org.interledger.core;
 
-import com.google.common.base.Preconditions;
 import org.immutables.value.Value;
 
 import java.util.Base64;
@@ -53,7 +52,9 @@ public interface SharedSecret {
 
   @Value.Check
   default SharedSecret validate() {
-    Preconditions.checkState(key().length == 32, "SharedSecret must be 32 bytes");
+    if (key().length != 32) {
+      throw new IllegalStateException(String.valueOf("SharedSecret must be 32 bytes"));
+    }
     return this;
   }
 
diff --git a/jackson-datatypes/src/main/java/org/interledger/quilt/jackson/InterledgerDeserializers.java b/jackson-datatypes/src/main/java/org/interledger/quilt/jackson/InterledgerDeserializers.java
index 2facfb8..06a813c 100644
--- a/jackson-datatypes/src/main/java/org/interledger/quilt/jackson/InterledgerDeserializers.java
+++ b/jackson-datatypes/src/main/java/org/interledger/quilt/jackson/InterledgerDeserializers.java
@@ -23,10 +23,13 @@ package org.interledger.quilt.jackson;
 import org.interledger.core.InterledgerAddress;
 import org.interledger.core.InterledgerCondition;
 import org.interledger.core.InterledgerFulfillment;
+import org.interledger.core.SharedSecret;
 import org.interledger.quilt.jackson.address.InterledgerAddressDeserializer;
 import org.interledger.quilt.jackson.conditions.ConditionDeserializer;
 import org.interledger.quilt.jackson.conditions.Encoding;
 import org.interledger.quilt.jackson.conditions.FulfillmentDeserializer;
+import org.interledger.quilt.jackson.sharedsecret.SharedSecretDeserializer;
+import org.interledger.quilt.jackson.sharedsecret.SharedSecretSerializer;
 
 import com.fasterxml.jackson.databind.BeanDescription;
 import com.fasterxml.jackson.databind.DeserializationConfig;
@@ -62,7 +65,9 @@ public class InterledgerDeserializers extends Deserializers.Base {
     if (type.hasRawClass(InterledgerFulfillment.class)) {
       return new FulfillmentDeserializer(cryptoConditionEncoding);
     }
-
+    if (type.hasRawClass(SharedSecret.class)) {
+      return new SharedSecretDeserializer();
+    }
     return null;
   }
 
diff --git a/jackson-datatypes/src/main/java/org/interledger/quilt/jackson/InterledgerSerializers.java b/jackson-datatypes/src/main/java/org/interledger/quilt/jackson/InterledgerSerializers.java
index 7278ef7..ebeda32 100644
--- a/jackson-datatypes/src/main/java/org/interledger/quilt/jackson/InterledgerSerializers.java
+++ b/jackson-datatypes/src/main/java/org/interledger/quilt/jackson/InterledgerSerializers.java
@@ -23,10 +23,12 @@ package org.interledger.quilt.jackson;
 import org.interledger.core.InterledgerAddress;
 import org.interledger.core.InterledgerCondition;
 import org.interledger.core.InterledgerFulfillment;
+import org.interledger.core.SharedSecret;
 import org.interledger.quilt.jackson.address.InterledgerAddressSerializer;
 import org.interledger.quilt.jackson.conditions.ConditionSerializer;
 import org.interledger.quilt.jackson.conditions.Encoding;
 import org.interledger.quilt.jackson.conditions.FulfillmentSerializer;
+import org.interledger.quilt.jackson.sharedsecret.SharedSecretSerializer;
 
 import com.fasterxml.jackson.databind.BeanDescription;
 import com.fasterxml.jackson.databind.JavaType;
@@ -61,6 +63,9 @@ public class InterledgerSerializers extends Serializers.Base {
     if (InterledgerFulfillment.class.isAssignableFrom(raw)) {
       return new FulfillmentSerializer(cryptoConditionEncoding);
     }
+    if (SharedSecret.class.isAssignableFrom(raw)) {
+      return new SharedSecretSerializer();
+    }
     return null;
   }
 
diff --git a/jackson-datatypes/src/main/java/org/interledger/quilt/jackson/sharedsecret/SharedSecretDeserializer.java b/jackson-datatypes/src/main/java/org/interledger/quilt/jackson/sharedsecret/SharedSecretDeserializer.java
new file mode 100644
index 0000000..0e2abfd
--- /dev/null
+++ b/jackson-datatypes/src/main/java/org/interledger/quilt/jackson/sharedsecret/SharedSecretDeserializer.java
@@ -0,0 +1,49 @@
+package org.interledger.quilt.jackson.sharedsecret;
+
+/*-
+ * ========================LICENSE_START=================================
+ * Interledger Jackson Datatypes
+ * %%
+ * Copyright (C) 2017 - 2018 Hyperledger and its contributors
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * =========================LICENSE_END==================================
+ */
+
+import org.interledger.core.SharedSecret;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.deser.std.FromStringDeserializer;
+
+/**
+ * An extension of {@link FromStringDeserializer} that deserializes a JSON string into an instance
+ * of {@link SharedSecret}.
+ */
+public class SharedSecretDeserializer extends FromStringDeserializer<SharedSecret> {
+
+  public static final SharedSecretDeserializer INSTANCE
+      = new SharedSecretDeserializer();
+
+  /**
+   * No-args Constructor.
+   */
+  public SharedSecretDeserializer() {
+    super(SharedSecret.class);
+  }
+
+  @Override
+  protected SharedSecret _deserialize(
+      final String value, final DeserializationContext deserializationContext
+  ) {
+    return SharedSecret.of(value);
+  }
+}
diff --git a/jackson-datatypes/src/main/java/org/interledger/quilt/jackson/sharedsecret/SharedSecretModule.java b/jackson-datatypes/src/main/java/org/interledger/quilt/jackson/sharedsecret/SharedSecretModule.java
new file mode 100644
index 0000000..3989766
--- /dev/null
+++ b/jackson-datatypes/src/main/java/org/interledger/quilt/jackson/sharedsecret/SharedSecretModule.java
@@ -0,0 +1,49 @@
+package org.interledger.quilt.jackson.sharedsecret;
+
+/*-
+ * ========================LICENSE_START=================================
+ * Interledger Jackson Datatypes
+ * %%
+ * Copyright (C) 2017 - 2018 Hyperledger and its contributors
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * =========================LICENSE_END==================================
+ */
+
+import org.interledger.core.SharedSecret;
+import com.fasterxml.jackson.core.Version;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+
+/**
+ * A Jackson {@link SimpleModule} for serializing and deserializing {@link SharedSecret}
+ * objects.
+ */
+public class SharedSecretModule extends SimpleModule {
+
+  private static final String NAME = SharedSecretModule.class.getName();
+
+  /**
+   * No-args Constructor.
+   */
+  public SharedSecretModule() {
+
+    super(
+        NAME,
+        new Version(1, 0, 0, null, "org.interledger", "jackson-datatype-shared-secret")
+    );
+
+    addSerializer(SharedSecret.class, SharedSecretSerializer.INSTANCE);
+    addDeserializer(SharedSecret.class, SharedSecretDeserializer.INSTANCE);
+  }
+
+}
diff --git a/jackson-datatypes/src/main/java/org/interledger/quilt/jackson/sharedsecret/SharedSecretSerializer.java b/jackson-datatypes/src/main/java/org/interledger/quilt/jackson/sharedsecret/SharedSecretSerializer.java
new file mode 100644
index 0000000..8ce695e
--- /dev/null
+++ b/jackson-datatypes/src/main/java/org/interledger/quilt/jackson/sharedsecret/SharedSecretSerializer.java
@@ -0,0 +1,48 @@
+package org.interledger.quilt.jackson.sharedsecret;
+
+/*-
+ * ========================LICENSE_START=================================
+ * Interledger Jackson Datatypes
+ * %%
+ * Copyright (C) 2017 - 2018 Hyperledger and its contributors
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * =========================LICENSE_END==================================
+ */
+
+import org.interledger.core.SharedSecret;
+import java.io.IOException;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.ser.std.StdScalarSerializer;
+
+/**
+ * Jackson Serializer for {@link SharedSecret}.
+ */
+public class SharedSecretSerializer extends StdScalarSerializer<SharedSecret> {
+
+  public static final SharedSecretSerializer INSTANCE = new SharedSecretSerializer();
+
+  /**
+   * No-args Constructor.
+   */
+  public SharedSecretSerializer() {
+    super(SharedSecret.class, false);
+  }
+
+  @Override
+  public void serialize(SharedSecret value, JsonGenerator gen, SerializerProvider provider)
+      throws IOException {
+    gen.writeString(value.value());
+  }
+}
diff --git a/spsp-parent/spsp-core/src/main/java/org/interledger/spsp/SharedSecret.java b/spsp-parent/spsp-core/src/main/java/org/interledger/spsp/SharedSecret.java
deleted file mode 100644
index 1d36a66..0000000
--- a/spsp-parent/spsp-core/src/main/java/org/interledger/spsp/SharedSecret.java
+++ /dev/null
@@ -1,64 +0,0 @@
-package org.interledger.spsp;
-
-import com.fasterxml.jackson.annotation.JsonValue;
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
-import com.fasterxml.jackson.databind.annotation.JsonSerialize;
-import com.google.common.base.Preconditions;
-import org.immutables.value.Value;
-
-import java.util.Base64;
-
-/**
- * Wrapper for a SPSP shared secret (32-byte, base64 encoded).
- *
- * @see "https://github.com/interledger/rfcs/blob/master/0009-simple-payment-setup-protocol/0009-simple-payment-setup-protocol.md"
- */
-@JsonSerialize(as = ImmutableSharedSecret.class)
-@JsonDeserialize(as = ImmutableSharedSecret.class)
-@Value.Immutable
-public interface SharedSecret {
-
-  static SharedSecret of(String base64EncodedKey) {
-    return ImmutableSharedSecret.of(base64EncodedKey);
-  }
-
-  static SharedSecret of(byte[] key) {
-    return ImmutableSharedSecret.of(Base64.getEncoder().withoutPadding().encodeToString(key));
-  }
-
-  /**
-   * Base64 encoded key for sending over the wire.
-   *
-   * @return A {@link String} containing {@link #key()} in a Base64 encoding.
-   */
-  @Value.Parameter
-  @JsonValue
-  String value();
-
-  /**
-   * 32-byte key for encryption/decryption within Java.
-   *
-   * @return key bytes
-   */
-  @Value.Derived
-  @Value.Auxiliary
-  default byte[] key() {
-    // both base64 and base64 url encoded keys are allowed by the rfc
-    try {
-      return Base64.getDecoder().decode(value());
-    } catch (IllegalArgumentException e) {
-      try {
-        return Base64.getUrlDecoder().decode(value());
-      } catch (IllegalArgumentException ex) {
-        throw new IllegalArgumentException("SharedSecret must be base64 encoded");
-      }
-    }
-  }
-
-  @Value.Check
-  default SharedSecret validate() {
-    Preconditions.checkState(key().length == 32, "SharedSecret must be 32 bytes");
-    return this;
-  }
-
-}
diff --git a/spsp-parent/spsp-core/src/main/java/org/interledger/spsp/StreamConnectionDetails.java b/spsp-parent/spsp-core/src/main/java/org/interledger/spsp/StreamConnectionDetails.java
index c609fce..073a03b 100644
--- a/spsp-parent/spsp-core/src/main/java/org/interledger/spsp/StreamConnectionDetails.java
+++ b/spsp-parent/spsp-core/src/main/java/org/interledger/spsp/StreamConnectionDetails.java
@@ -1,6 +1,7 @@
 package org.interledger.spsp;
 
 import org.interledger.core.InterledgerAddress;
+import org.interledger.core.SharedSecret;
 import org.interledger.spsp.ImmutableStreamConnectionDetails.Builder;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
diff --git a/stream-parent/stream-client/src/main/java/org/interledger/stream/sender/SimpleStreamSender.java b/stream-parent/stream-client/src/main/java/org/interledger/stream/sender/SimpleStreamSender.java
index 7af04b0..d7b8de6 100644
--- a/stream-parent/stream-client/src/main/java/org/interledger/stream/sender/SimpleStreamSender.java
+++ b/stream-parent/stream-client/src/main/java/org/interledger/stream/sender/SimpleStreamSender.java
@@ -15,6 +15,7 @@ import org.interledger.core.InterledgerPacketType;
 import org.interledger.core.InterledgerPreparePacket;
 import org.interledger.core.InterledgerRejectPacket;
 import org.interledger.core.InterledgerResponsePacket;
+import org.interledger.core.SharedSecret;
 import org.interledger.encoding.asn.framework.CodecContext;
 import org.interledger.link.Link;
 import org.interledger.stream.SendMoneyResult;
@@ -24,7 +25,6 @@ import org.interledger.stream.StreamConnectionId;
 import org.interledger.stream.StreamPacket;
 import org.interledger.stream.StreamUtils;
 import org.interledger.stream.crypto.JavaxStreamEncryptionService;
-import org.interledger.stream.crypto.SharedSecret;
 import org.interledger.stream.crypto.StreamEncryptionService;
 import org.interledger.stream.frames.ConnectionCloseFrame;
 import org.interledger.stream.frames.ConnectionNewAddressFrame;
diff --git a/stream-parent/stream-client/src/main/java/org/interledger/stream/sender/StreamSender.java b/stream-parent/stream-client/src/main/java/org/interledger/stream/sender/StreamSender.java
index 150a141..fa31302 100644
--- a/stream-parent/stream-client/src/main/java/org/interledger/stream/sender/StreamSender.java
+++ b/stream-parent/stream-client/src/main/java/org/interledger/stream/sender/StreamSender.java
@@ -1,8 +1,8 @@
 package org.interledger.stream.sender;
 
 import org.interledger.core.InterledgerAddress;
+import org.interledger.core.SharedSecret;
 import org.interledger.stream.SendMoneyResult;
-import org.interledger.stream.crypto.SharedSecret;
 
 import com.google.common.primitives.UnsignedLong;
 
diff --git a/stream-parent/stream-core/src/main/java/org/interledger/stream/Ids.java b/stream-parent/stream-core/src/main/java/org/interledger/stream/Ids.java
index 5a02567..7218133 100644
--- a/stream-parent/stream-core/src/main/java/org/interledger/stream/Ids.java
+++ b/stream-parent/stream-core/src/main/java/org/interledger/stream/Ids.java
@@ -1,9 +1,9 @@
 package org.interledger.stream;
 
 import org.interledger.core.InterledgerAddress;
+import org.interledger.core.SharedSecret;
 import org.interledger.core.Wrapped;
 import org.interledger.core.Wrapper;
-import org.interledger.stream.crypto.SharedSecret;
 
 import com.google.common.hash.Hashing;
 import org.immutables.value.Value;
diff --git a/stream-parent/stream-core/src/main/java/org/interledger/stream/StreamConnection.java b/stream-parent/stream-core/src/main/java/org/interledger/stream/StreamConnection.java
index b8408e9..37b8aca 100644
--- a/stream-parent/stream-core/src/main/java/org/interledger/stream/StreamConnection.java
+++ b/stream-parent/stream-core/src/main/java/org/interledger/stream/StreamConnection.java
@@ -1,7 +1,7 @@
 package org.interledger.stream;
 
 import org.interledger.core.InterledgerAddress;
-import org.interledger.stream.crypto.SharedSecret;
+import org.interledger.core.SharedSecret;
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.primitives.UnsignedLong;
diff --git a/stream-parent/stream-core/src/main/java/org/interledger/stream/StreamUtils.java b/stream-parent/stream-core/src/main/java/org/interledger/stream/StreamUtils.java
index 7b4e1d8..2dba83f 100644
--- a/stream-parent/stream-core/src/main/java/org/interledger/stream/StreamUtils.java
+++ b/stream-parent/stream-core/src/main/java/org/interledger/stream/StreamUtils.java
@@ -2,8 +2,8 @@ package org.interledger.stream;
 
 import org.interledger.core.InterledgerCondition;
 import org.interledger.core.InterledgerFulfillment;
+import org.interledger.core.SharedSecret;
 import org.interledger.stream.crypto.Random;
-import org.interledger.stream.crypto.SharedSecret;
 
 import com.google.common.hash.Hashing;
 import com.google.common.primitives.UnsignedLong;
diff --git a/stream-parent/stream-core/src/main/java/org/interledger/stream/crypto/JavaxStreamEncryptionService.java b/stream-parent/stream-core/src/main/java/org/interledger/stream/crypto/JavaxStreamEncryptionService.java
index 4bee753..c085a43 100644
--- a/stream-parent/stream-core/src/main/java/org/interledger/stream/crypto/JavaxStreamEncryptionService.java
+++ b/stream-parent/stream-core/src/main/java/org/interledger/stream/crypto/JavaxStreamEncryptionService.java
@@ -1,5 +1,6 @@
 package org.interledger.stream.crypto;
 
+import org.interledger.core.SharedSecret;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.hash.Hashing;
 
diff --git a/stream-parent/stream-core/src/main/java/org/interledger/stream/crypto/StreamEncryptionService.java b/stream-parent/stream-core/src/main/java/org/interledger/stream/crypto/StreamEncryptionService.java
index a5c69f0..81cb409 100644
--- a/stream-parent/stream-core/src/main/java/org/interledger/stream/crypto/StreamEncryptionService.java
+++ b/stream-parent/stream-core/src/main/java/org/interledger/stream/crypto/StreamEncryptionService.java
@@ -1,5 +1,7 @@
 package org.interledger.stream.crypto;
 
+import org.interledger.core.SharedSecret;
+
 /**
  * A helper class for performing encryption and decryption operations required by the STREAM protocol.
  *
diff --git a/stream-parent/stream-receiver/src/main/java/org/interledger/stream/receiver/SpspStreamConnectionGenerator.java b/stream-parent/stream-receiver/src/main/java/org/interledger/stream/receiver/SpspStreamConnectionGenerator.java
index 166b6c8..413da80 100644
--- a/stream-parent/stream-receiver/src/main/java/org/interledger/stream/receiver/SpspStreamConnectionGenerator.java
+++ b/stream-parent/stream-receiver/src/main/java/org/interledger/stream/receiver/SpspStreamConnectionGenerator.java
@@ -1,8 +1,8 @@
 package org.interledger.stream.receiver;
 
 import org.interledger.core.InterledgerAddress;
+import org.interledger.core.SharedSecret;
 import org.interledger.spsp.ImmutableStreamConnectionDetails.Builder;
-import org.interledger.spsp.SharedSecret;
 import org.interledger.spsp.StreamConnectionDetails;
 import org.interledger.stream.StreamException;
 import org.interledger.stream.crypto.Random;
diff --git a/stream-parent/stream-receiver/src/main/java/org/interledger/stream/receiver/StatelessStreamReceiver.java b/stream-parent/stream-receiver/src/main/java/org/interledger/stream/receiver/StatelessStreamReceiver.java
index b856b86..f12a315 100644
--- a/stream-parent/stream-receiver/src/main/java/org/interledger/stream/receiver/StatelessStreamReceiver.java
+++ b/stream-parent/stream-receiver/src/main/java/org/interledger/stream/receiver/StatelessStreamReceiver.java
@@ -8,13 +8,13 @@ import org.interledger.core.InterledgerPacketType;
 import org.interledger.core.InterledgerPreparePacket;
 import org.interledger.core.InterledgerRejectPacket;
 import org.interledger.core.InterledgerResponsePacket;
+import org.interledger.core.SharedSecret;
 import org.interledger.encoding.asn.framework.CodecContext;
 import org.interledger.spsp.StreamConnectionDetails;
 import org.interledger.stream.Denomination;
 import org.interledger.stream.StreamException;
 import org.interledger.stream.StreamPacket;
 import org.interledger.stream.StreamUtils;
-import org.interledger.stream.crypto.SharedSecret;
 import org.interledger.stream.crypto.StreamEncryptionService;
 import org.interledger.stream.frames.ConnectionAssetDetailsFrame;
 import org.interledger.stream.frames.ConnectionCloseFrame;
@@ -83,7 +83,7 @@ public class StatelessStreamReceiver implements StreamReceiver {
     Objects.requireNonNull(receiverAddress);
 
     // Will throw if there's an error...
-    final org.interledger.spsp.SharedSecret spspSharedSecret = this.streamConnectionGenerator
+    final SharedSecret spspSharedSecret = this.streamConnectionGenerator
         .deriveSecretFromAddress(serverSecretSupplier, preparePacket.getDestination());
     final SharedSecret streamSharedSecret = SharedSecret.of(spspSharedSecret.key());
 
diff --git a/stream-parent/stream-receiver/src/main/java/org/interledger/stream/receiver/StreamConnectionGenerator.java b/stream-parent/stream-receiver/src/main/java/org/interledger/stream/receiver/StreamConnectionGenerator.java
index 71628b8..2246d6a 100644
--- a/stream-parent/stream-receiver/src/main/java/org/interledger/stream/receiver/StreamConnectionGenerator.java
+++ b/stream-parent/stream-receiver/src/main/java/org/interledger/stream/receiver/StreamConnectionGenerator.java
@@ -1,7 +1,7 @@
 package org.interledger.stream.receiver;
 
 import org.interledger.core.InterledgerAddress;
-import org.interledger.spsp.SharedSecret;
+import org.interledger.core.SharedSecret;
 import org.interledger.spsp.StreamConnectionDetails;
 import org.interledger.stream.StreamException;
 


Test patch:
diff --git a/jackson-datatypes/src/test/java/org/interledger/quilt/jackson/InterledgerModuleTest.java b/jackson-datatypes/src/test/java/org/interledger/quilt/jackson/InterledgerModuleTest.java
index eb11414..0739077 100644
--- a/jackson-datatypes/src/test/java/org/interledger/quilt/jackson/InterledgerModuleTest.java
+++ b/jackson-datatypes/src/test/java/org/interledger/quilt/jackson/InterledgerModuleTest.java
@@ -23,10 +23,12 @@ package org.interledger.quilt.jackson;
 import org.interledger.core.InterledgerAddress;
 import org.interledger.core.InterledgerCondition;
 import org.interledger.core.InterledgerFulfillment;
+import org.interledger.core.SharedSecret;
 import org.interledger.quilt.jackson.conditions.Encoding;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import org.junit.Before;
 import org.junit.Test;
@@ -36,8 +38,10 @@ import org.junit.runners.Parameterized.Parameters;
 
 import java.io.IOException;
 import java.util.Arrays;
+import java.util.Base64;
 import java.util.Collection;
 import java.util.Objects;
+import java.util.Random;
 
 import static org.assertj.core.api.Assertions.assertThat;
 
@@ -137,6 +141,29 @@ public class InterledgerModuleTest {
     assertThat(actualAddressContainer.getCondition()).isEqualTo(condition);
   }
 
+  @Test
+  public void sharedSecretSerializeDeserialize() throws JsonProcessingException {
+    byte[] key = new byte[32];
+    new Random().nextBytes(key);
+    SharedSecret sharedSecret = SharedSecret.of(key);
+
+    String jsonValue = objectMapper.writeValueAsString(sharedSecret);
+    SharedSecret fromJackson = objectMapper.readValue(jsonValue, SharedSecret.class);
+    assertThat(fromJackson).isEqualTo(sharedSecret);
+  }
+
+  @Test
+  public void sharedSecretBase64SerializeDeserialize() throws JsonProcessingException {
+    byte[] key = new byte[32];
+    new Random().nextBytes(key);
+    String base64 = Base64.getEncoder().encodeToString(key);
+    SharedSecret sharedSecret = SharedSecret.of(key);
+
+    String jsonValue = objectMapper.writeValueAsString(sharedSecret);
+    SharedSecret fromJackson = objectMapper.readValue(jsonValue, SharedSecret.class);
+    assertThat(fromJackson).isEqualTo(sharedSecret);
+  }
+
   private static class InterledgerContainer {
 
     @JsonProperty("ledger_prefix")
diff --git a/spsp-parent/spsp-client/src/test/java/org/interledger/spsp/client/rust/InterledgerRustNodeClientTest.java b/spsp-parent/spsp-client/src/test/java/org/interledger/spsp/client/rust/InterledgerRustNodeClientTest.java
index d559a44..acb60be 100644
--- a/spsp-parent/spsp-client/src/test/java/org/interledger/spsp/client/rust/InterledgerRustNodeClientTest.java
+++ b/spsp-parent/spsp-client/src/test/java/org/interledger/spsp/client/rust/InterledgerRustNodeClientTest.java
@@ -6,8 +6,8 @@ import static org.assertj.core.api.Assertions.assertThat;
 import com.github.tomakehurst.wiremock.junit.WireMockRule;
 import okhttp3.OkHttpClient;
 import org.interledger.core.InterledgerAddress;
+import org.interledger.core.SharedSecret;
 import org.interledger.spsp.PaymentPointer;
-import org.interledger.spsp.SharedSecret;
 import org.interledger.spsp.StreamConnectionDetails;
 import org.interledger.spsp.client.InvalidReceiverClientException;
 import org.interledger.spsp.client.SpspClient;
diff --git a/spsp-parent/spsp-core/src/test/java/org/interledger/spsp/SharedSecretTest.java b/spsp-parent/spsp-core/src/test/java/org/interledger/spsp/SharedSecretTest.java
deleted file mode 100644
index 7ccf117..0000000
--- a/spsp-parent/spsp-core/src/test/java/org/interledger/spsp/SharedSecretTest.java
+++ /dev/null
@@ -1,83 +0,0 @@
-package org.interledger.spsp;
-
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-
-import java.util.Base64;
-import java.util.Random;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class SharedSecretTest {
-
-  @Rule
-  public ExpectedException thrown = ExpectedException.none();
-
-  @Test
-  public void fromBase64Key() {
-    Random random = new Random();
-    for (int i = 0; i < 100; i++) {
-      byte[] key = new byte[32];
-      random.nextBytes(key);
-      String base64 = Base64.getEncoder().encodeToString(key);
-      SharedSecret sharedSecret = SharedSecret.of(base64);
-      assertThat(sharedSecret.value()).isEqualTo(base64);
-      assertThat(sharedSecret.key()).isEqualTo(key);
-    }
-  }
-
-  @Test
-  public void fromBase64UrlKey() {
-    Random random = new Random();
-    for (int i = 0; i < 100; i++) {
-      byte[] key = new byte[32];
-      random.nextBytes(key);
-      String base64Url = Base64.getUrlEncoder().encodeToString(key);
-      SharedSecret sharedSecret = SharedSecret.of(base64Url);
-      assertThat(sharedSecret.value()).isEqualTo(base64Url);
-      assertThat(sharedSecret.key()).isEqualTo(key);
-    }
-  }
-
-  @Test
-  public void exceptionIfNotBase64() {
-    thrown.expect(IllegalArgumentException.class);
-    thrown.expectMessage("SharedSecret must be base64 encoded");
-    SharedSecret.of("!");
-  }
-
-  @Test
-  public void exceptionIfLessThan32Bytes() {
-    final byte[] smallKey = new byte[31];
-    String base64 = Base64.getEncoder().encodeToString(smallKey);
-    thrown.expect(IllegalStateException.class);
-    thrown.expectMessage("SharedSecret must be 32 bytes");
-    SharedSecret.of(base64);
-  }
-
-  @Test
-  public void exceptionIfMoreThan32Bytes() {
-    final byte[] bigKey = new byte[33];
-    String base64 = Base64.getEncoder().encodeToString(bigKey);
-    thrown.expect(IllegalStateException.class);
-    thrown.expectMessage("SharedSecret must be 32 bytes");
-    SharedSecret.of(base64);
-  }
-
-  @Test
-  public void jacksonCanDoItsThing() throws JsonProcessingException {
-    byte[] key = new byte[32];
-    new Random().nextBytes(key);
-    String base64 = Base64.getEncoder().encodeToString(key);
-    SharedSecret sharedSecret = SharedSecret.of(base64);
-
-    ObjectMapper objectMapper = new ObjectMapper();
-    String jsonValue = objectMapper.writeValueAsString(sharedSecret);
-    SharedSecret fromJackson = objectMapper.readValue(jsonValue, SharedSecret.class);
-    assertThat(fromJackson).isEqualTo(sharedSecret);
-  }
-
-}
\ No newline at end of file
diff --git a/stream-parent/stream-client/src/test/java/org/interledger/stream/sender/SendMoneyAggregatorTest.java b/stream-parent/stream-client/src/test/java/org/interledger/stream/sender/SendMoneyAggregatorTest.java
index b6eed8c..1e67488 100644
--- a/stream-parent/stream-client/src/test/java/org/interledger/stream/sender/SendMoneyAggregatorTest.java
+++ b/stream-parent/stream-client/src/test/java/org/interledger/stream/sender/SendMoneyAggregatorTest.java
@@ -7,11 +7,11 @@ import static org.mockito.Mockito.when;
 
 import org.interledger.core.InterledgerAddress;
 import org.interledger.core.InterledgerRejectPacket;
+import org.interledger.core.SharedSecret;
 import org.interledger.encoding.asn.framework.CodecContext;
 import org.interledger.link.Link;
 import org.interledger.stream.StreamConnection;
 import org.interledger.stream.StreamConnectionClosedException;
-import org.interledger.stream.crypto.SharedSecret;
 import org.interledger.stream.crypto.StreamEncryptionService;
 import org.interledger.stream.sender.SimpleStreamSender.SendMoneyAggregator;
 
diff --git a/stream-parent/stream-client/src/test/java/org/interledger/stream/sender/SimpleStreamSenderIT.java b/stream-parent/stream-client/src/test/java/org/interledger/stream/sender/SimpleStreamSenderIT.java
index a5dbf04..867d95f 100644
--- a/stream-parent/stream-client/src/test/java/org/interledger/stream/sender/SimpleStreamSenderIT.java
+++ b/stream-parent/stream-client/src/test/java/org/interledger/stream/sender/SimpleStreamSenderIT.java
@@ -5,6 +5,7 @@ import static org.assertj.core.api.Assertions.assertThat;
 
 import org.interledger.codecs.ilp.InterledgerCodecContextFactory;
 import org.interledger.core.InterledgerAddress;
+import org.interledger.core.SharedSecret;
 import org.interledger.link.Link;
 import org.interledger.link.LinkId;
 import org.interledger.link.http.IlpOverHttpLink;
@@ -22,7 +23,6 @@ import org.interledger.spsp.client.rust.ImmutableAccount;
 import org.interledger.spsp.client.rust.InterledgerRustNodeClient;
 import org.interledger.stream.SendMoneyResult;
 import org.interledger.stream.crypto.JavaxStreamEncryptionService;
-import org.interledger.stream.crypto.SharedSecret;
 
 import com.fasterxml.jackson.core.JsonGenerator;
 import com.fasterxml.jackson.core.json.JsonWriteFeature;
diff --git a/stream-parent/stream-core/src/test/java/org/interledger/stream/StreamConnectionIdTest.java b/stream-parent/stream-core/src/test/java/org/interledger/stream/StreamConnectionIdTest.java
index 60d9d49..941602a 100644
--- a/stream-parent/stream-core/src/test/java/org/interledger/stream/StreamConnectionIdTest.java
+++ b/stream-parent/stream-core/src/test/java/org/interledger/stream/StreamConnectionIdTest.java
@@ -3,7 +3,7 @@ package org.interledger.stream;
 import static org.mockito.Mockito.mock;
 
 import org.interledger.core.InterledgerAddress;
-import org.interledger.stream.crypto.SharedSecret;
+import org.interledger.core.SharedSecret;
 
 import org.junit.Rule;
 import org.junit.Test;
diff --git a/stream-parent/stream-core/src/test/java/org/interledger/stream/StreamConnectionTest.java b/stream-parent/stream-core/src/test/java/org/interledger/stream/StreamConnectionTest.java
index 5bbc074..f7e97ca 100644
--- a/stream-parent/stream-core/src/test/java/org/interledger/stream/StreamConnectionTest.java
+++ b/stream-parent/stream-core/src/test/java/org/interledger/stream/StreamConnectionTest.java
@@ -4,8 +4,8 @@ import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Mockito.mock;
 
 import org.interledger.core.InterledgerAddress;
+import org.interledger.core.SharedSecret;
 import org.interledger.stream.StreamConnection.StreamConnectionState;
-import org.interledger.stream.crypto.SharedSecret;
 
 import com.google.common.collect.Lists;
 import com.google.common.primitives.UnsignedLong;
diff --git a/stream-parent/stream-core/src/test/java/org/interledger/stream/StreamUtilsTest.java b/stream-parent/stream-core/src/test/java/org/interledger/stream/StreamUtilsTest.java
index fbae918..a768ded 100644
--- a/stream-parent/stream-core/src/test/java/org/interledger/stream/StreamUtilsTest.java
+++ b/stream-parent/stream-core/src/test/java/org/interledger/stream/StreamUtilsTest.java
@@ -5,7 +5,7 @@ import static org.junit.Assert.fail;
 
 import org.interledger.core.InterledgerCondition;
 import org.interledger.core.InterledgerFulfillment;
-import org.interledger.stream.crypto.SharedSecret;
+import org.interledger.core.SharedSecret;
 
 import com.google.common.primitives.UnsignedLong;
 import org.junit.Test;
diff --git a/stream-parent/stream-core/src/test/java/org/interledger/stream/crypto/JavaxStreamEncryptionServiceTest.java b/stream-parent/stream-core/src/test/java/org/interledger/stream/crypto/JavaxStreamEncryptionServiceTest.java
index e157d2b..8a01a12 100644
--- a/stream-parent/stream-core/src/test/java/org/interledger/stream/crypto/JavaxStreamEncryptionServiceTest.java
+++ b/stream-parent/stream-core/src/test/java/org/interledger/stream/crypto/JavaxStreamEncryptionServiceTest.java
@@ -2,6 +2,7 @@ package org.interledger.stream.crypto;
 
 import static org.assertj.core.api.Assertions.assertThat;
 
+import org.interledger.core.SharedSecret;
 import org.junit.Before;
 import org.junit.Test;
 
diff --git a/stream-parent/stream-core/src/test/java/org/interledger/stream/crypto/SharedSecretTest.java b/stream-parent/stream-core/src/test/java/org/interledger/stream/crypto/SharedSecretTest.java
index 0d677b7..31091d8 100644
--- a/stream-parent/stream-core/src/test/java/org/interledger/stream/crypto/SharedSecretTest.java
+++ b/stream-parent/stream-core/src/test/java/org/interledger/stream/crypto/SharedSecretTest.java
@@ -2,6 +2,7 @@ package org.interledger.stream.crypto;
 
 import static org.assertj.core.api.Assertions.assertThat;
 
+import org.interledger.core.SharedSecret;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
diff --git a/stream-parent/stream-receiver/src/test/java/org/interledger/stream/receiver/SenderReceiverTest.java b/stream-parent/stream-receiver/src/test/java/org/interledger/stream/receiver/SenderReceiverTest.java
index 4460b5d..075d59d 100644
--- a/stream-parent/stream-receiver/src/test/java/org/interledger/stream/receiver/SenderReceiverTest.java
+++ b/stream-parent/stream-receiver/src/test/java/org/interledger/stream/receiver/SenderReceiverTest.java
@@ -5,12 +5,12 @@ import static org.assertj.core.api.Assertions.assertThat;
 import org.interledger.codecs.stream.StreamCodecContextFactory;
 import org.interledger.core.Immutable;
 import org.interledger.core.InterledgerAddress;
+import org.interledger.core.SharedSecret;
 import org.interledger.link.Link;
 import org.interledger.spsp.StreamConnectionDetails;
 import org.interledger.stream.Denomination;
 import org.interledger.stream.SendMoneyResult;
 import org.interledger.stream.crypto.JavaxStreamEncryptionService;
-import org.interledger.stream.crypto.SharedSecret;
 import org.interledger.stream.crypto.StreamEncryptionService;
 import org.interledger.stream.receiver.testutils.SimulatedILPv4Network;
 import org.interledger.stream.receiver.testutils.SimulatedPathConditions;
diff --git a/stream-parent/stream-receiver/src/test/java/org/interledger/stream/receiver/StatelessStreamReceiverTest.java b/stream-parent/stream-receiver/src/test/java/org/interledger/stream/receiver/StatelessStreamReceiverTest.java
index 0280c00..74ba093 100644
--- a/stream-parent/stream-receiver/src/test/java/org/interledger/stream/receiver/StatelessStreamReceiverTest.java
+++ b/stream-parent/stream-receiver/src/test/java/org/interledger/stream/receiver/StatelessStreamReceiverTest.java
@@ -14,6 +14,7 @@ import org.interledger.core.InterledgerErrorCode;
 import org.interledger.core.InterledgerPacketType;
 import org.interledger.core.InterledgerPreparePacket;
 import org.interledger.core.InterledgerRejectPacket;
+import org.interledger.core.SharedSecret;
 import org.interledger.encoding.asn.framework.CodecContext;
 import org.interledger.spsp.StreamConnectionDetails;
 import org.interledger.stream.Denomination;
@@ -22,7 +23,6 @@ import org.interledger.stream.StreamException;
 import org.interledger.stream.StreamPacket;
 import org.interledger.stream.StreamUtils;
 import org.interledger.stream.crypto.JavaxStreamEncryptionService;
-import org.interledger.stream.crypto.SharedSecret;
 import org.interledger.stream.crypto.StreamEncryptionService;
 import org.interledger.stream.frames.ConnectionAssetDetailsFrame;
 import org.interledger.stream.frames.ConnectionNewAddressFrame;