mirror of
https://github.com/corda/corda.git
synced 2024-12-19 04:57:58 +00:00
CORDA-3750: Reimplement Corda's Crypto object for use inside the sandbox. (#6193)
* CORDA-3750: Use hand-written sandbox Crypto object that delegates to the node. * CORDA-3750: Add integration test for deterministic CashIssueAndPayment flow. * Tidy up generics for Array instances. * Upgrade to DJVM 1.1-RC04.
This commit is contained in:
parent
4ed57506c8
commit
6ebc6e9b16
@ -30,7 +30,7 @@ snakeYamlVersion=1.19
|
||||
caffeineVersion=2.7.0
|
||||
metricsVersion=4.1.0
|
||||
metricsNewRelicVersion=1.1.1
|
||||
djvmVersion=1.1-RC03
|
||||
djvmVersion=1.1-RC04
|
||||
deterministicRtVersion=1.0-RC02
|
||||
openSourceBranch=https://github.com/corda/corda/blob/release/os/4.4
|
||||
openSourceSamplesBranch=https://github.com/corda/samples/blob/release-V4
|
||||
|
@ -304,6 +304,11 @@ quasar {
|
||||
|
||||
jar {
|
||||
baseName 'corda-node'
|
||||
exclude 'sandbox/java/**'
|
||||
exclude 'sandbox/org/**'
|
||||
exclude 'sandbox/net/corda/core/crypto/SecureHash.class'
|
||||
exclude 'sandbox/net/corda/core/crypto/SignatureScheme.class'
|
||||
exclude 'sandbox/net/corda/core/crypto/TransactionSignature.class'
|
||||
manifest {
|
||||
attributes('Corda-Deterministic-Runtime': configurations.jdkRt.singleFile.name)
|
||||
attributes('Corda-Deterministic-Classpath': configurations.deterministic.collect { it.name }.join(' '))
|
||||
|
@ -27,12 +27,12 @@ private const val TX_PRIVACY_SALT = 7
|
||||
private const val TX_NETWORK_PARAMETERS = 8
|
||||
private const val TX_REFERENCES = 9
|
||||
|
||||
class LtxFactory : Function<Array<Any?>, LedgerTransaction> {
|
||||
class LtxFactory : Function<Array<out Any?>, LedgerTransaction> {
|
||||
|
||||
@Suppress("unchecked_cast")
|
||||
override fun apply(txArgs: Array<Any?>): LedgerTransaction {
|
||||
override fun apply(txArgs: Array<out Any?>): LedgerTransaction {
|
||||
return LedgerTransaction.createForSandbox(
|
||||
inputs = (txArgs[TX_INPUTS] as Array<Array<Any?>>).map { it.toStateAndRef() },
|
||||
inputs = (txArgs[TX_INPUTS] as Array<Array<out Any?>>).map { it.toStateAndRef() },
|
||||
outputs = (txArgs[TX_OUTPUTS] as? List<TransactionState<ContractState>>) ?: emptyList(),
|
||||
commands = (txArgs[TX_COMMANDS] as? List<CommandWithParties<CommandData>>) ?: emptyList(),
|
||||
attachments = (txArgs[TX_ATTACHMENTS] as? List<Attachment>) ?: emptyList(),
|
||||
@ -41,7 +41,7 @@ class LtxFactory : Function<Array<Any?>, LedgerTransaction> {
|
||||
timeWindow = txArgs[TX_TIME_WINDOW] as? TimeWindow,
|
||||
privacySalt = txArgs[TX_PRIVACY_SALT] as PrivacySalt,
|
||||
networkParameters = txArgs[TX_NETWORK_PARAMETERS] as NetworkParameters,
|
||||
references = (txArgs[TX_REFERENCES] as Array<Array<Any?>>).map { it.toStateAndRef() }
|
||||
references = (txArgs[TX_REFERENCES] as Array<Array<out Any?>>).map { it.toStateAndRef() }
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,68 @@
|
||||
package net.corda.node.services
|
||||
|
||||
import net.corda.core.messaging.startFlow
|
||||
import net.corda.core.utilities.OpaqueBytes
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import net.corda.finance.DOLLARS
|
||||
import net.corda.finance.flows.CashIssueAndPaymentFlow
|
||||
import net.corda.node.DeterministicSourcesRule
|
||||
import net.corda.testing.core.ALICE_NAME
|
||||
import net.corda.testing.core.DUMMY_NOTARY_NAME
|
||||
import net.corda.testing.core.singleIdentity
|
||||
import net.corda.testing.driver.DriverParameters
|
||||
import net.corda.testing.driver.driver
|
||||
import net.corda.testing.driver.internal.incrementalPortAllocation
|
||||
import net.corda.testing.node.NotarySpec
|
||||
import net.corda.testing.node.internal.findCordapp
|
||||
import org.junit.ClassRule
|
||||
import org.junit.Test
|
||||
import org.junit.jupiter.api.assertDoesNotThrow
|
||||
|
||||
@Suppress("FunctionName")
|
||||
class DeterministicCashIssueAndPaymentTest {
|
||||
companion object {
|
||||
val logger = loggerFor<DeterministicCashIssueAndPaymentTest>()
|
||||
|
||||
@ClassRule
|
||||
@JvmField
|
||||
val djvmSources = DeterministicSourcesRule()
|
||||
|
||||
@JvmField
|
||||
val CASH_AMOUNT = 500.DOLLARS
|
||||
|
||||
fun parametersFor(djvmSources: DeterministicSourcesRule): DriverParameters {
|
||||
return DriverParameters(
|
||||
portAllocation = incrementalPortAllocation(),
|
||||
startNodesInProcess = false,
|
||||
notarySpecs = listOf(NotarySpec(DUMMY_NOTARY_NAME, validating = true)),
|
||||
cordappsForAllNodes = listOf(
|
||||
findCordapp("net.corda.finance.contracts"),
|
||||
findCordapp("net.corda.finance.workflows")
|
||||
),
|
||||
djvmBootstrapSource = djvmSources.bootstrap,
|
||||
djvmCordaSource = djvmSources.corda
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeout = 300_000)
|
||||
fun `test DJVM can issue cash`() {
|
||||
val reference = OpaqueBytes.of(0x01)
|
||||
driver(parametersFor(djvmSources)) {
|
||||
val alice = startNode(providedName = ALICE_NAME).getOrThrow()
|
||||
val aliceParty = alice.nodeInfo.singleIdentity()
|
||||
val notaryParty = notaryHandles.single().identity
|
||||
val txId = assertDoesNotThrow {
|
||||
alice.rpc.startFlow(::CashIssueAndPaymentFlow,
|
||||
CASH_AMOUNT,
|
||||
reference,
|
||||
aliceParty,
|
||||
false,
|
||||
notaryParty
|
||||
).returnValue.getOrThrow()
|
||||
}
|
||||
logger.info("TX-ID: {}", txId)
|
||||
}
|
||||
}
|
||||
}
|
8
node/src/main/java/sandbox/java/lang/CharSequence.java
Normal file
8
node/src/main/java/sandbox/java/lang/CharSequence.java
Normal file
@ -0,0 +1,8 @@
|
||||
package sandbox.java.lang;
|
||||
|
||||
/**
|
||||
* This is a dummy class that implements just enough of {@link java.lang.CharSequence}
|
||||
* to allow us to compile {@link sandbox.net.corda.core.crypto.Crypto}.
|
||||
*/
|
||||
public interface CharSequence extends java.lang.CharSequence {
|
||||
}
|
8
node/src/main/java/sandbox/java/lang/Comparable.java
Normal file
8
node/src/main/java/sandbox/java/lang/Comparable.java
Normal file
@ -0,0 +1,8 @@
|
||||
package sandbox.java.lang;
|
||||
|
||||
/**
|
||||
* This is a dummy class that implements just enough of {@link java.lang.Comparable}
|
||||
* to allow us to compile {@link sandbox.net.corda.core.crypto.Crypto}.
|
||||
*/
|
||||
public interface Comparable<T> extends java.lang.Comparable<T> {
|
||||
}
|
8
node/src/main/java/sandbox/java/lang/Number.java
Normal file
8
node/src/main/java/sandbox/java/lang/Number.java
Normal file
@ -0,0 +1,8 @@
|
||||
package sandbox.java.lang;
|
||||
|
||||
/**
|
||||
* This is a dummy class that implements just enough of {@link java.lang.Number}
|
||||
* to allow us to compile {@link sandbox.net.corda.core.crypto.Crypto}.
|
||||
*/
|
||||
public class Number extends Object {
|
||||
}
|
8
node/src/main/java/sandbox/java/math/BigInteger.java
Normal file
8
node/src/main/java/sandbox/java/math/BigInteger.java
Normal file
@ -0,0 +1,8 @@
|
||||
package sandbox.java.math;
|
||||
|
||||
/**
|
||||
* This is a dummy class that implements just enough of {@link java.math.BigInteger}
|
||||
* to allow us to compile {@link sandbox.net.corda.core.crypto.Crypto}.
|
||||
*/
|
||||
public class BigInteger extends sandbox.java.lang.Number {
|
||||
}
|
13
node/src/main/java/sandbox/java/security/Key.java
Normal file
13
node/src/main/java/sandbox/java/security/Key.java
Normal file
@ -0,0 +1,13 @@
|
||||
package sandbox.java.security;
|
||||
|
||||
import sandbox.java.lang.String;
|
||||
|
||||
/**
|
||||
* This is a dummy class that implements just enough of {@link java.security.Key}
|
||||
* to allow us to compile {@link sandbox.net.corda.core.crypto.Crypto}.
|
||||
*/
|
||||
public interface Key {
|
||||
String getAlgorithm();
|
||||
String getFormat();
|
||||
byte[] getEncoded();
|
||||
}
|
8
node/src/main/java/sandbox/java/security/KeyPair.java
Normal file
8
node/src/main/java/sandbox/java/security/KeyPair.java
Normal file
@ -0,0 +1,8 @@
|
||||
package sandbox.java.security;
|
||||
|
||||
/**
|
||||
* This is a dummy class that implements just enough of {@link java.security.KeyPair}
|
||||
* to allow us to compile {@link sandbox.net.corda.core.crypto.Crypto}.
|
||||
*/
|
||||
public final class KeyPair extends sandbox.java.lang.Object implements java.io.Serializable {
|
||||
}
|
8
node/src/main/java/sandbox/java/security/PrivateKey.java
Normal file
8
node/src/main/java/sandbox/java/security/PrivateKey.java
Normal file
@ -0,0 +1,8 @@
|
||||
package sandbox.java.security;
|
||||
|
||||
/**
|
||||
* This is a dummy class that implements just enough of {@link java.security.PrivateKey}
|
||||
* to allow us to compile {@link sandbox.net.corda.core.crypto.Crypto}.
|
||||
*/
|
||||
public interface PrivateKey extends Key {
|
||||
}
|
8
node/src/main/java/sandbox/java/security/PublicKey.java
Normal file
8
node/src/main/java/sandbox/java/security/PublicKey.java
Normal file
@ -0,0 +1,8 @@
|
||||
package sandbox.java.security;
|
||||
|
||||
/**
|
||||
* This is a dummy class that implements just enough of {@link java.security.PublicKey}
|
||||
* to allow us to compile {@link sandbox.net.corda.core.crypto.Crypto}.
|
||||
*/
|
||||
public interface PublicKey extends Key {
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package sandbox.java.security.spec;
|
||||
|
||||
/**
|
||||
* This is a dummy class that implements just enough of {@link java.security.spec.AlgorithmParameterSpec}
|
||||
* to allow us to compile {@link sandbox.net.corda.core.crypto.Crypto}.
|
||||
*/
|
||||
public interface AlgorithmParameterSpec {
|
||||
}
|
16
node/src/main/java/sandbox/java/util/ArrayList.java
Normal file
16
node/src/main/java/sandbox/java/util/ArrayList.java
Normal file
@ -0,0 +1,16 @@
|
||||
package sandbox.java.util;
|
||||
|
||||
/**
|
||||
* This is a dummy class that implements just enough of {@link java.util.ArrayList}
|
||||
* to allow us to compile {@link sandbox.net.corda.core.crypto.Crypto}.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class ArrayList<T> extends sandbox.java.lang.Object implements List<T> {
|
||||
public ArrayList(int size) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(T item) {
|
||||
throw new UnsupportedOperationException("Dummy class - not implemented");
|
||||
}
|
||||
}
|
9
node/src/main/java/sandbox/java/util/List.java
Normal file
9
node/src/main/java/sandbox/java/util/List.java
Normal file
@ -0,0 +1,9 @@
|
||||
package sandbox.java.util;
|
||||
|
||||
/**
|
||||
* This is a dummy class that implements just enough of {@link java.util.List}
|
||||
* to allow us to compile {@link sandbox.net.corda.core.crypto.Crypto}.
|
||||
*/
|
||||
public interface List<T> {
|
||||
boolean add(T item);
|
||||
}
|
62
node/src/main/java/sandbox/net/corda/core/crypto/DJVM.java
Normal file
62
node/src/main/java/sandbox/net/corda/core/crypto/DJVM.java
Normal file
@ -0,0 +1,62 @@
|
||||
package sandbox.net.corda.core.crypto;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import sandbox.java.lang.Integer;
|
||||
import sandbox.java.lang.String;
|
||||
import sandbox.java.util.ArrayList;
|
||||
import sandbox.java.util.List;
|
||||
import sandbox.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Helper class for {@link sandbox.net.corda.core.crypto.Crypto}.
|
||||
* Deliberately package-private.
|
||||
*/
|
||||
final class DJVM {
|
||||
private DJVM() {}
|
||||
|
||||
@NotNull
|
||||
static SignatureScheme toDJVM(@NotNull net.corda.core.crypto.SignatureScheme scheme) {
|
||||
// The AlgorithmParameterSpec is deliberately left as null
|
||||
// because it is computationally expensive to generate these
|
||||
// objects inside the sandbox every time.
|
||||
return new SignatureScheme(
|
||||
scheme.getSchemeNumberID(),
|
||||
String.toDJVM(scheme.getSchemeCodeName()),
|
||||
toDJVM(scheme.getSignatureOID()),
|
||||
toDJVM(scheme.getAlternativeOIDs()),
|
||||
String.toDJVM(scheme.getProviderName()),
|
||||
String.toDJVM(scheme.getAlgorithmName()),
|
||||
String.toDJVM(scheme.getSignatureName()),
|
||||
null,
|
||||
Integer.toDJVM(scheme.getKeySize()),
|
||||
String.toDJVM(scheme.getDesc())
|
||||
);
|
||||
}
|
||||
|
||||
static org.bouncycastle.asn1.x509.AlgorithmIdentifier fromDJVM(@NotNull AlgorithmIdentifier oid) {
|
||||
try {
|
||||
return org.bouncycastle.asn1.x509.AlgorithmIdentifier.getInstance(oid.getEncoded());
|
||||
} catch (IOException e) {
|
||||
throw sandbox.java.lang.DJVM.toRuleViolationError(e);
|
||||
}
|
||||
}
|
||||
|
||||
static AlgorithmIdentifier toDJVM(@NotNull org.bouncycastle.asn1.x509.AlgorithmIdentifier oid) {
|
||||
try {
|
||||
return AlgorithmIdentifier.getInstance(oid.getEncoded());
|
||||
} catch (IOException e) {
|
||||
throw sandbox.java.lang.DJVM.toRuleViolationError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
static List<AlgorithmIdentifier> toDJVM(@NotNull java.util.List<org.bouncycastle.asn1.x509.AlgorithmIdentifier> list) {
|
||||
ArrayList<AlgorithmIdentifier> djvmList = new ArrayList<>(list.size());
|
||||
for (org.bouncycastle.asn1.x509.AlgorithmIdentifier oid : list) {
|
||||
djvmList.add(toDJVM(oid));
|
||||
}
|
||||
return djvmList;
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
package sandbox.net.corda.core.crypto;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import sandbox.java.lang.Object;
|
||||
import sandbox.java.lang.String;
|
||||
import sandbox.java.security.PublicKey;
|
||||
|
||||
/**
|
||||
* We shall delegate as much cryptography as possible to Corda's
|
||||
* underlying {@link net.corda.core.crypto.Crypto} object and its
|
||||
* {@link java.security.Provider} classes. This wrapper only needs
|
||||
* to implement {@link #equals} and {@link #hashCode}.
|
||||
*/
|
||||
final class DJVMPublicKey extends Object implements PublicKey {
|
||||
private final java.security.PublicKey underlying;
|
||||
private final String algorithm;
|
||||
private final String format;
|
||||
private final int hashCode;
|
||||
|
||||
DJVMPublicKey(@NotNull java.security.PublicKey underlying) {
|
||||
this.underlying = underlying;
|
||||
this.algorithm = String.toDJVM(underlying.getAlgorithm());
|
||||
this.format = String.toDJVM(underlying.getFormat());
|
||||
this.hashCode = underlying.hashCode();
|
||||
}
|
||||
|
||||
java.security.PublicKey getUnderlying() {
|
||||
return underlying;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(java.lang.Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
} else if (!(other instanceof DJVMPublicKey)) {
|
||||
return false;
|
||||
} else {
|
||||
return underlying.equals(((DJVMPublicKey) other).underlying);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlgorithm() {
|
||||
return algorithm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFormat() {
|
||||
return format;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getEncoded() {
|
||||
return underlying.getEncoded();
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package sandbox.org.bouncycastle.asn1;
|
||||
|
||||
/**
|
||||
* This is a dummy class that implements just enough of {@link org.bouncycastle.asn1.ASN1Encodable}
|
||||
* to allow us to compile {@link sandbox.net.corda.core.crypto.Crypto}.
|
||||
*/
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public interface ASN1Encodable {
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package sandbox.org.bouncycastle.asn1;
|
||||
|
||||
import sandbox.java.lang.Object;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* This is a dummy class that implements just enough of {@link org.bouncycastle.asn1.ASN1Object}
|
||||
* to allow us to compile {@link sandbox.net.corda.core.crypto.Crypto}.
|
||||
*/
|
||||
@SuppressWarnings("RedundantThrows")
|
||||
public class ASN1Object extends Object implements ASN1Encodable {
|
||||
public byte[] getEncoded() throws IOException {
|
||||
throw new UnsupportedOperationException("Dummy class - not implemented");
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package sandbox.org.bouncycastle.asn1.x509;
|
||||
|
||||
import sandbox.org.bouncycastle.asn1.ASN1Object;
|
||||
|
||||
/**
|
||||
* This is a dummy class that implements just enough of {@link org.bouncycastle.asn1.x509.AlgorithmIdentifier}
|
||||
* to allow us to compile {@link sandbox.net.corda.core.crypto.Crypto}.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class AlgorithmIdentifier extends ASN1Object {
|
||||
public static AlgorithmIdentifier getInstance(Object obj) {
|
||||
throw new UnsupportedOperationException("Dummy class - not implemented");
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package sandbox.org.bouncycastle.asn1.x509;
|
||||
|
||||
import sandbox.org.bouncycastle.asn1.ASN1Object;
|
||||
|
||||
/**
|
||||
* This is a dummy class that implements just enough of {@link org.bouncycastle.asn1.x509.SubjectPublicKeyInfo}
|
||||
* to allow us to compile {@link sandbox.net.corda.core.crypto.Crypto}.
|
||||
*/
|
||||
public class SubjectPublicKeyInfo extends ASN1Object {
|
||||
}
|
@ -31,7 +31,7 @@ class Serializer(
|
||||
* [net.corda.node.djvm.LtxFactory] to be transformed finally to
|
||||
* a list of [net.corda.core.contracts.StateAndRef] objects,
|
||||
*/
|
||||
fun deserialize(stateRefs: List<SerializedStateAndRef>): Array<Array<Any?>> {
|
||||
fun deserialize(stateRefs: List<SerializedStateAndRef>): Array<Array<out Any?>> {
|
||||
return stateRefs.map {
|
||||
arrayOf(deserialize(it.serializedState), deserialize(it.ref.serialize()))
|
||||
}.toTypedArray()
|
||||
|
@ -47,7 +47,20 @@ class DeterministicVerifierFactoryService(
|
||||
ConstructorForDeserialization::class.java,
|
||||
DeprecatedConstructorForDeserialization::class.java
|
||||
),
|
||||
bootstrapSource = bootstrapSource
|
||||
bootstrapSource = bootstrapSource,
|
||||
overrideClasses = setOf(
|
||||
/**
|
||||
* These classes are all duplicated into the sandbox
|
||||
* without the DJVM modifying their byte-code first.
|
||||
* The goal is to delegate cryptographic operations
|
||||
* out to the Node rather than perform them inside
|
||||
* the sandbox, because this is MUCH FASTER.
|
||||
*/
|
||||
sandbox.net.corda.core.crypto.Crypto::class.java.name,
|
||||
"sandbox.net.corda.core.crypto.DJVM",
|
||||
"sandbox.net.corda.core.crypto.DJVMPublicKey",
|
||||
"sandbox.net.corda.core.crypto.internal.ProviderMapKt"
|
||||
)
|
||||
)
|
||||
|
||||
baseSandboxConfiguration = SandboxConfiguration.createFor(
|
||||
|
261
node/src/main/kotlin/sandbox/net/corda/core/crypto/Crypto.kt
Normal file
261
node/src/main/kotlin/sandbox/net/corda/core/crypto/Crypto.kt
Normal file
@ -0,0 +1,261 @@
|
||||
package sandbox.net.corda.core.crypto
|
||||
|
||||
import sandbox.net.corda.core.crypto.DJVM.fromDJVM
|
||||
import sandbox.net.corda.core.crypto.DJVM.toDJVM
|
||||
import sandbox.java.lang.String
|
||||
import sandbox.java.lang.doCatch
|
||||
import sandbox.java.math.BigInteger
|
||||
import sandbox.java.security.KeyPair
|
||||
import sandbox.java.security.PrivateKey
|
||||
import sandbox.java.security.PublicKey
|
||||
import sandbox.java.util.ArrayList
|
||||
import sandbox.java.util.List
|
||||
import sandbox.java.lang.Object
|
||||
import sandbox.org.bouncycastle.asn1.x509.AlgorithmIdentifier
|
||||
import sandbox.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo
|
||||
import java.security.GeneralSecurityException
|
||||
import java.security.SignatureException
|
||||
import java.security.spec.InvalidKeySpecException
|
||||
|
||||
/**
|
||||
* This is a hand-written "drop-in" replacement for the version of
|
||||
* [net.corda.core.crypto.Crypto] found inside core-deterministic.
|
||||
* This class is used in the DJVM sandbox instead of transforming
|
||||
* the byte-code for [net.corda.core.crypto.Crypto], with the goal
|
||||
* of not needing to instantiate some EXPENSIVE elliptic curve
|
||||
* cryptography classes inside every single sandbox.
|
||||
*
|
||||
* The downside is that this class MUST manually be kept consistent
|
||||
* with the DJVM's byte-code rewriting rules.
|
||||
*/
|
||||
@Suppress("unused", "unused_parameter", "TooManyFunctions")
|
||||
object Crypto : Object() {
|
||||
@JvmField
|
||||
val RSA_SHA256: SignatureScheme = toDJVM(net.corda.core.crypto.Crypto.RSA_SHA256)
|
||||
|
||||
@JvmField
|
||||
val ECDSA_SECP256K1_SHA256: SignatureScheme = toDJVM(net.corda.core.crypto.Crypto.ECDSA_SECP256K1_SHA256)
|
||||
|
||||
@JvmField
|
||||
val ECDSA_SECP256R1_SHA256: SignatureScheme = toDJVM(net.corda.core.crypto.Crypto.ECDSA_SECP256R1_SHA256)
|
||||
|
||||
@JvmField
|
||||
val EDDSA_ED25519_SHA512: SignatureScheme = toDJVM(net.corda.core.crypto.Crypto.EDDSA_ED25519_SHA512)
|
||||
|
||||
@JvmField
|
||||
val SPHINCS256_SHA256: SignatureScheme = toDJVM(net.corda.core.crypto.Crypto.SPHINCS256_SHA256)
|
||||
|
||||
@JvmField
|
||||
val COMPOSITE_KEY: SignatureScheme = toDJVM(net.corda.core.crypto.Crypto.COMPOSITE_KEY)
|
||||
|
||||
@JvmField
|
||||
val DEFAULT_SIGNATURE_SCHEME = EDDSA_ED25519_SHA512
|
||||
|
||||
/**
|
||||
* We can use the unsandboxed versions of [Map] and [List] here
|
||||
* because the [underlyingSchemes] and [djvmSchemes] fields are
|
||||
* private and not exposed to the rest of the sandbox.
|
||||
*/
|
||||
private val underlyingSchemes: Map<kotlin.String, net.corda.core.crypto.SignatureScheme>
|
||||
= net.corda.core.crypto.Crypto.supportedSignatureSchemes()
|
||||
.associateBy(net.corda.core.crypto.SignatureScheme::schemeCodeName)
|
||||
private val djvmSchemes: Map<String, SignatureScheme> = listOf(
|
||||
RSA_SHA256,
|
||||
ECDSA_SECP256K1_SHA256,
|
||||
ECDSA_SECP256R1_SHA256,
|
||||
EDDSA_ED25519_SHA512,
|
||||
SPHINCS256_SHA256,
|
||||
COMPOSITE_KEY
|
||||
).associateByTo(LinkedHashMap(), SignatureScheme::schemeCodeName)
|
||||
|
||||
private fun findUnderlyingSignatureScheme(signatureScheme: SignatureScheme): net.corda.core.crypto.SignatureScheme {
|
||||
return net.corda.core.crypto.Crypto.findSignatureScheme(String.fromDJVM(signatureScheme.schemeCodeName))
|
||||
}
|
||||
|
||||
private fun PublicKey.toUnderlyingKey(): java.security.PublicKey {
|
||||
return (this as? DJVMPublicKey ?: throw sandbox.java.lang.fail("Unsupported key ${this::class.java.name}")).underlying
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun supportedSignatureSchemes(): List<SignatureScheme> {
|
||||
val schemes = ArrayList<SignatureScheme>(djvmSchemes.size)
|
||||
for (scheme in djvmSchemes.values) {
|
||||
schemes.add(scheme)
|
||||
}
|
||||
return schemes
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun isSupportedSignatureScheme(signatureScheme: SignatureScheme): Boolean {
|
||||
return String.fromDJVM(signatureScheme.schemeCodeName) in underlyingSchemes
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun findSignatureScheme(schemeCodeName: String): SignatureScheme {
|
||||
return djvmSchemes[schemeCodeName]
|
||||
?: throw IllegalArgumentException("Unsupported key/algorithm for schemeCodeName: $schemeCodeName")
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun findSignatureScheme(schemeNumberID: Int): SignatureScheme {
|
||||
val underlyingScheme = net.corda.core.crypto.Crypto.findSignatureScheme(schemeNumberID)
|
||||
return findSignatureScheme(String.toDJVM(underlyingScheme.schemeCodeName))
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun findSignatureScheme(key: PublicKey): SignatureScheme {
|
||||
val underlyingScheme = net.corda.core.crypto.Crypto.findSignatureScheme(key.toUnderlyingKey())
|
||||
return findSignatureScheme(String.toDJVM(underlyingScheme.schemeCodeName))
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun findSignatureScheme(algorithm: AlgorithmIdentifier): SignatureScheme {
|
||||
val underlyingScheme = net.corda.core.crypto.Crypto.findSignatureScheme(fromDJVM(algorithm))
|
||||
return findSignatureScheme(String.toDJVM(underlyingScheme.schemeCodeName))
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun findSignatureScheme(key: PrivateKey): SignatureScheme {
|
||||
throw sandbox.java.lang.failApi("Crypto.findSignatureScheme(PrivateKey)")
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun decodePrivateKey(signatureScheme: SignatureScheme, encodedKey: ByteArray): PrivateKey {
|
||||
throw sandbox.java.lang.failApi("Crypto.decodePrivateKey(SignatureScheme, byte[])")
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun decodePublicKey(encodedKey: ByteArray): PublicKey {
|
||||
val underlying = try {
|
||||
net.corda.core.crypto.Crypto.decodePublicKey(encodedKey)
|
||||
} catch (e: InvalidKeySpecException) {
|
||||
throw sandbox.java.lang.fromDJVM(doCatch(e))
|
||||
}
|
||||
return DJVMPublicKey(underlying)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun decodePublicKey(schemeCodeName: String, encodedKey: ByteArray): PublicKey {
|
||||
val underlying = try {
|
||||
net.corda.core.crypto.Crypto.decodePublicKey(String.fromDJVM(schemeCodeName), encodedKey)
|
||||
} catch (e: InvalidKeySpecException) {
|
||||
throw sandbox.java.lang.fromDJVM(doCatch(e))
|
||||
}
|
||||
return DJVMPublicKey(underlying)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun decodePublicKey(signatureScheme: SignatureScheme, encodedKey: ByteArray): PublicKey {
|
||||
return decodePublicKey(signatureScheme.schemeCodeName, encodedKey)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun deriveKeyPair(signatureScheme: SignatureScheme, privateKey: PrivateKey, seed: ByteArray): KeyPair {
|
||||
throw sandbox.java.lang.failApi("Crypto.deriveKeyPair(SignatureScheme, PrivateKey, byte[])")
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun deriveKeyPair(privateKey: PrivateKey, seed: ByteArray): KeyPair {
|
||||
throw sandbox.java.lang.failApi("Crypto.deriveKeyPair(PrivateKey, byte[])")
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun deriveKeyPairFromEntropy(signatureScheme: SignatureScheme, entropy: BigInteger): KeyPair {
|
||||
throw sandbox.java.lang.failApi("Crypto.deriveKeyPairFromEntropy(SignatureScheme, BigInteger)")
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun deriveKeyPairFromEntropy(entropy: BigInteger): KeyPair {
|
||||
throw sandbox.java.lang.failApi("Crypto.deriveKeyPairFromEntropy(BigInteger)")
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun doVerify(schemeCodeName: String, publicKey: PublicKey, signatureData: ByteArray, clearData: ByteArray): Boolean {
|
||||
val underlyingKey = publicKey.toUnderlyingKey()
|
||||
return try {
|
||||
net.corda.core.crypto.Crypto.doVerify(String.fromDJVM(schemeCodeName), underlyingKey, signatureData, clearData)
|
||||
} catch (e: GeneralSecurityException) {
|
||||
throw sandbox.java.lang.fromDJVM(doCatch(e))
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun doVerify(publicKey: PublicKey, signatureData: ByteArray, clearData: ByteArray): Boolean {
|
||||
val underlyingKey = publicKey.toUnderlyingKey()
|
||||
return try {
|
||||
net.corda.core.crypto.Crypto.doVerify(underlyingKey, signatureData, clearData)
|
||||
} catch (e: GeneralSecurityException) {
|
||||
throw sandbox.java.lang.fromDJVM(doCatch(e))
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun doVerify(signatureScheme: SignatureScheme, publicKey: PublicKey, signatureData: ByteArray, clearData: ByteArray): Boolean {
|
||||
val underlyingScheme = findUnderlyingSignatureScheme(signatureScheme)
|
||||
val underlyingKey = publicKey.toUnderlyingKey()
|
||||
return try {
|
||||
net.corda.core.crypto.Crypto.doVerify(underlyingScheme, underlyingKey, signatureData, clearData)
|
||||
} catch (e: GeneralSecurityException) {
|
||||
throw sandbox.java.lang.fromDJVM(doCatch(e))
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun doVerify(txId: SecureHash, transactionSignature: TransactionSignature): Boolean {
|
||||
throw sandbox.java.lang.failApi("Crypto.doVerify(SecureHash, TransactionSignature)")
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun isValid(txId: SecureHash, transactionSignature: TransactionSignature): Boolean {
|
||||
throw sandbox.java.lang.failApi("Crypto.isValid(SecureHash, TransactionSignature)")
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun isValid(publicKey: PublicKey, signatureData: ByteArray, clearData: ByteArray): Boolean {
|
||||
val underlyingKey = publicKey.toUnderlyingKey()
|
||||
return try {
|
||||
net.corda.core.crypto.Crypto.isValid(underlyingKey, signatureData, clearData)
|
||||
} catch (e: SignatureException) {
|
||||
throw sandbox.java.lang.fromDJVM(doCatch(e))
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun isValid(signatureScheme: SignatureScheme, publicKey: PublicKey, signatureData: ByteArray, clearData: ByteArray): Boolean {
|
||||
val underlyingScheme = findUnderlyingSignatureScheme(signatureScheme)
|
||||
val underlyingKey = publicKey.toUnderlyingKey()
|
||||
return try {
|
||||
net.corda.core.crypto.Crypto.isValid(underlyingScheme, underlyingKey, signatureData, clearData)
|
||||
} catch (e: SignatureException) {
|
||||
throw sandbox.java.lang.fromDJVM(doCatch(e))
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun publicKeyOnCurve(signatureScheme: SignatureScheme, publicKey: PublicKey): Boolean {
|
||||
val underlyingScheme = findUnderlyingSignatureScheme(signatureScheme)
|
||||
val underlyingKey = publicKey.toUnderlyingKey()
|
||||
return net.corda.core.crypto.Crypto.publicKeyOnCurve(underlyingScheme, underlyingKey)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun validatePublicKey(key: PublicKey): Boolean {
|
||||
return net.corda.core.crypto.Crypto.validatePublicKey(key.toUnderlyingKey())
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun toSupportedPublicKey(key: SubjectPublicKeyInfo): PublicKey {
|
||||
return decodePublicKey(key.encoded)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun toSupportedPublicKey(key: PublicKey): PublicKey {
|
||||
val underlyingKey = key.toUnderlyingKey()
|
||||
val supportedKey = net.corda.core.crypto.Crypto.toSupportedPublicKey(underlyingKey)
|
||||
return if (supportedKey === underlyingKey) {
|
||||
key
|
||||
} else {
|
||||
DJVMPublicKey(supportedKey)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package sandbox.net.corda.core.crypto
|
||||
|
||||
/**
|
||||
* This is a dummy class that implements just enough of [net.corda.core.crypto.SecureHash]
|
||||
* to allow us to compile [sandbox.net.corda.core.crypto.Crypto].
|
||||
*/
|
||||
@Suppress("unused_parameter")
|
||||
sealed class SecureHash(bytes: ByteArray) : sandbox.java.lang.Object()
|
@ -0,0 +1,26 @@
|
||||
package sandbox.net.corda.core.crypto
|
||||
|
||||
import sandbox.java.lang.String
|
||||
import sandbox.java.lang.Integer
|
||||
import sandbox.java.lang.Object
|
||||
import sandbox.java.security.spec.AlgorithmParameterSpec
|
||||
import sandbox.java.util.List
|
||||
import sandbox.org.bouncycastle.asn1.x509.AlgorithmIdentifier
|
||||
|
||||
/**
|
||||
* This is a dummy class that implements just enough of [net.corda.core.crypto.SignatureScheme]
|
||||
* to allow us to compile [sandbox.net.corda.core.crypto.Crypto].
|
||||
*/
|
||||
@Suppress("unused")
|
||||
class SignatureScheme(
|
||||
val schemeNumberID: Int,
|
||||
val schemeCodeName: String,
|
||||
val signatureOID: AlgorithmIdentifier,
|
||||
val alternativeOIDs: List<AlgorithmIdentifier>,
|
||||
val providerName: String,
|
||||
val algorithmName: String,
|
||||
val signatureName: String,
|
||||
val algSpec: AlgorithmParameterSpec?,
|
||||
val keySize: Integer?,
|
||||
val desc: String
|
||||
) : Object()
|
@ -0,0 +1,7 @@
|
||||
package sandbox.net.corda.core.crypto
|
||||
|
||||
/**
|
||||
* This is a dummy class that implements just enough of [net.corda.core.crypto.TransactionSignature]
|
||||
* to allow us to compile [sandbox.net.corda.core.crypto.Crypto].
|
||||
*/
|
||||
class TransactionSignature : sandbox.java.lang.Object()
|
@ -0,0 +1,8 @@
|
||||
package sandbox.net.corda.core.crypto.internal
|
||||
|
||||
/**
|
||||
* THIS FILE IS DELIBERATELY EMPTY, APART FROM A SINGLE DUMMY VALUE.
|
||||
* KOTLIN WILL NOT CREATE A CLASS IF THIS FILE IS COMPLETELY EMPTY.
|
||||
*/
|
||||
@Suppress("unused")
|
||||
private const val DUMMY_VALUE = 0
|
@ -19,8 +19,7 @@ class SandboxPublicKeySerializer(
|
||||
taskFactory: Function<Class<out Function<*, *>>, out Function<in Any?, out Any?>>
|
||||
) : CustomSerializer.Implements<Any>(classLoader.toSandboxAnyClass(PublicKey::class.java)) {
|
||||
@Suppress("unchecked_cast")
|
||||
private val decoder: Function<ByteArray, out Any?>
|
||||
= taskFactory.apply(PublicKeyDecoder::class.java) as Function<ByteArray, out Any?>
|
||||
private val decoder = taskFactory.apply(PublicKeyDecoder::class.java) as Function<ByteArray, out Any?>
|
||||
|
||||
override val schemaForDocumentation: Schema = Schema(emptyList())
|
||||
|
||||
|
@ -8,6 +8,7 @@ import net.corda.core.serialization.CordaSerializable
|
||||
import net.corda.core.serialization.internal._contextSerializationEnv
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.serialization.djvm.SandboxType.KOTLIN
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.extension.ExtendWith
|
||||
@ -69,13 +70,18 @@ class DeserializePublicKeyTest : TestBase(KOTLIN) {
|
||||
sandbox {
|
||||
_contextSerializationEnv.set(createSandboxSerializationEnv(classLoader))
|
||||
|
||||
val sandboxKey = data.deserializeFor(classLoader)
|
||||
val sandboxData = data.deserializeFor(classLoader)
|
||||
|
||||
val taskFactory = classLoader.createRawTaskFactory()
|
||||
val showPublicKey = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowPublicKey::class.java)
|
||||
val result = showPublicKey.apply(sandboxKey) ?: fail("Result cannot be null")
|
||||
val taskFactory = classLoader.createRawTaskFactory().compose(classLoader.createSandboxFunction())
|
||||
val showPublicKey = taskFactory.apply(ShowPublicKey::class.java)
|
||||
val result = showPublicKey.apply(sandboxData) ?: fail("Result cannot be null")
|
||||
|
||||
assertEquals(ShowPublicKey().apply(compositeData), result.toString())
|
||||
|
||||
val sandboxKey = taskFactory.apply(GetPublicKey::class.java)
|
||||
.apply(sandboxData) ?: fail("PublicKey cannot be null")
|
||||
assertThat(sandboxKey::class.java.name)
|
||||
.isEqualTo("sandbox." + CompositeKey::class.java.name)
|
||||
}
|
||||
}
|
||||
|
||||
@ -86,6 +92,12 @@ class DeserializePublicKeyTest : TestBase(KOTLIN) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class GetPublicKey : Function<PublicKeyData, PublicKey> {
|
||||
override fun apply(data: PublicKeyData): PublicKey {
|
||||
return data.key
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@CordaSerializable
|
||||
|
Loading…
Reference in New Issue
Block a user