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:
Chris Rankin 2020-05-26 15:46:29 +01:00 committed by GitHub
parent 4ed57506c8
commit 6ebc6e9b16
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 694 additions and 13 deletions

View File

@ -30,7 +30,7 @@ snakeYamlVersion=1.19
caffeineVersion=2.7.0 caffeineVersion=2.7.0
metricsVersion=4.1.0 metricsVersion=4.1.0
metricsNewRelicVersion=1.1.1 metricsNewRelicVersion=1.1.1
djvmVersion=1.1-RC03 djvmVersion=1.1-RC04
deterministicRtVersion=1.0-RC02 deterministicRtVersion=1.0-RC02
openSourceBranch=https://github.com/corda/corda/blob/release/os/4.4 openSourceBranch=https://github.com/corda/corda/blob/release/os/4.4
openSourceSamplesBranch=https://github.com/corda/samples/blob/release-V4 openSourceSamplesBranch=https://github.com/corda/samples/blob/release-V4

View File

@ -304,6 +304,11 @@ quasar {
jar { jar {
baseName 'corda-node' 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 { manifest {
attributes('Corda-Deterministic-Runtime': configurations.jdkRt.singleFile.name) attributes('Corda-Deterministic-Runtime': configurations.jdkRt.singleFile.name)
attributes('Corda-Deterministic-Classpath': configurations.deterministic.collect { it.name }.join(' ')) attributes('Corda-Deterministic-Classpath': configurations.deterministic.collect { it.name }.join(' '))

View File

@ -27,12 +27,12 @@ private const val TX_PRIVACY_SALT = 7
private const val TX_NETWORK_PARAMETERS = 8 private const val TX_NETWORK_PARAMETERS = 8
private const val TX_REFERENCES = 9 private const val TX_REFERENCES = 9
class LtxFactory : Function<Array<Any?>, LedgerTransaction> { class LtxFactory : Function<Array<out Any?>, LedgerTransaction> {
@Suppress("unchecked_cast") @Suppress("unchecked_cast")
override fun apply(txArgs: Array<Any?>): LedgerTransaction { override fun apply(txArgs: Array<out Any?>): LedgerTransaction {
return LedgerTransaction.createForSandbox( 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(), outputs = (txArgs[TX_OUTPUTS] as? List<TransactionState<ContractState>>) ?: emptyList(),
commands = (txArgs[TX_COMMANDS] as? List<CommandWithParties<CommandData>>) ?: emptyList(), commands = (txArgs[TX_COMMANDS] as? List<CommandWithParties<CommandData>>) ?: emptyList(),
attachments = (txArgs[TX_ATTACHMENTS] as? List<Attachment>) ?: 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, timeWindow = txArgs[TX_TIME_WINDOW] as? TimeWindow,
privacySalt = txArgs[TX_PRIVACY_SALT] as PrivacySalt, privacySalt = txArgs[TX_PRIVACY_SALT] as PrivacySalt,
networkParameters = txArgs[TX_NETWORK_PARAMETERS] as NetworkParameters, 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() }
) )
} }

View File

@ -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)
}
}
}

View 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 {
}

View 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> {
}

View 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 {
}

View 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 {
}

View 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();
}

View 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 {
}

View 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 {
}

View 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 {
}

View File

@ -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 {
}

View 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");
}
}

View 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);
}

View 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;
}
}

View File

@ -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();
}
}

View File

@ -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 {
}

View File

@ -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");
}
}

View File

@ -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");
}
}

View File

@ -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 {
}

View File

@ -31,7 +31,7 @@ class Serializer(
* [net.corda.node.djvm.LtxFactory] to be transformed finally to * [net.corda.node.djvm.LtxFactory] to be transformed finally to
* a list of [net.corda.core.contracts.StateAndRef] objects, * 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 { return stateRefs.map {
arrayOf(deserialize(it.serializedState), deserialize(it.ref.serialize())) arrayOf(deserialize(it.serializedState), deserialize(it.ref.serialize()))
}.toTypedArray() }.toTypedArray()

View File

@ -47,7 +47,20 @@ class DeterministicVerifierFactoryService(
ConstructorForDeserialization::class.java, ConstructorForDeserialization::class.java,
DeprecatedConstructorForDeserialization::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( baseSandboxConfiguration = SandboxConfiguration.createFor(

View 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)
}
}
}

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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

View File

@ -19,8 +19,7 @@ class SandboxPublicKeySerializer(
taskFactory: Function<Class<out Function<*, *>>, out Function<in Any?, out Any?>> taskFactory: Function<Class<out Function<*, *>>, out Function<in Any?, out Any?>>
) : CustomSerializer.Implements<Any>(classLoader.toSandboxAnyClass(PublicKey::class.java)) { ) : CustomSerializer.Implements<Any>(classLoader.toSandboxAnyClass(PublicKey::class.java)) {
@Suppress("unchecked_cast") @Suppress("unchecked_cast")
private val decoder: Function<ByteArray, out Any?> private val decoder = taskFactory.apply(PublicKeyDecoder::class.java) as Function<ByteArray, out Any?>
= taskFactory.apply(PublicKeyDecoder::class.java) as Function<ByteArray, out Any?>
override val schemaForDocumentation: Schema = Schema(emptyList()) override val schemaForDocumentation: Schema = Schema(emptyList())

View File

@ -8,6 +8,7 @@ import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.internal._contextSerializationEnv import net.corda.core.serialization.internal._contextSerializationEnv
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.serialization.djvm.SandboxType.KOTLIN 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.Assertions.assertEquals
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith import org.junit.jupiter.api.extension.ExtendWith
@ -69,13 +70,18 @@ class DeserializePublicKeyTest : TestBase(KOTLIN) {
sandbox { sandbox {
_contextSerializationEnv.set(createSandboxSerializationEnv(classLoader)) _contextSerializationEnv.set(createSandboxSerializationEnv(classLoader))
val sandboxKey = data.deserializeFor(classLoader) val sandboxData = data.deserializeFor(classLoader)
val taskFactory = classLoader.createRawTaskFactory() val taskFactory = classLoader.createRawTaskFactory().compose(classLoader.createSandboxFunction())
val showPublicKey = taskFactory.compose(classLoader.createSandboxFunction()).apply(ShowPublicKey::class.java) val showPublicKey = taskFactory.apply(ShowPublicKey::class.java)
val result = showPublicKey.apply(sandboxKey) ?: fail("Result cannot be null") val result = showPublicKey.apply(sandboxData) ?: fail("Result cannot be null")
assertEquals(ShowPublicKey().apply(compositeData), result.toString()) 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 @CordaSerializable