Skip to content

Commit

Permalink
keymanager: Fix e2e tests for ephemeral keys
Browse files Browse the repository at this point in the history
  • Loading branch information
peternose committed Aug 18, 2022
1 parent af18566 commit a50d16b
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 49 deletions.
2 changes: 0 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package runtime
import (
"context"
"fmt"
"reflect"

beacon "github.com/oasisprotocol/oasis-core/go/beacon/api"
"github.com/oasisprotocol/oasis-core/go/common"
Expand Down Expand Up @@ -42,13 +43,10 @@ func (sc *kmKeyGenerationImpl) Clone() scenario.Scenario {
}

func (sc *kmKeyGenerationImpl) Run(childEnv *env.Env) error {
// Start the network, but no need to start the client. Just ensure it
// is synced.
ctx := context.Background()
if err := sc.runtimeImpl.startNetworkAndTestClient(ctx, childEnv); err != nil {
return err
}

// Wait for the client to exit.
if err := sc.waitTestClientOnly(); err != nil {
if err := sc.runtimeImpl.startNetworkAndWaitForClientSync(ctx); err != nil {
return err
}

Expand All @@ -63,7 +61,7 @@ func (sc *kmKeyGenerationImpl) Run(childEnv *env.Env) error {

// Data needed for encryption and decryption.
keyPairID := "key pair id"
plaintext := "The quick brown fox jumps over the lazy dog"
plaintext := []byte("The quick brown fox jumps over the lazy dog")

epoch, err := sc.Net.Controller().Beacon.GetEpoch(ctx, consensus.HeightLatest)
if err != nil {
Expand Down Expand Up @@ -101,7 +99,7 @@ func (sc *kmKeyGenerationImpl) Run(childEnv *env.Env) error {
if err != nil {
return fmt.Errorf("failed to decrypt ciphertext: %w", err)
}
if decrypted != plaintext {
if !reflect.DeepEqual(decrypted, plaintext) {
return fmt.Errorf("decrypted ciphertext does match the plaintext (got: '%s', expected: '%s')", decrypted, plaintext)
}

Expand All @@ -117,7 +115,7 @@ func (sc *kmKeyGenerationImpl) Run(childEnv *env.Env) error {
keyPairID,
ciphertext,
)
if err == nil && decrypted == plaintext {
if err == nil && reflect.DeepEqual(decrypted, plaintext) {
return fmt.Errorf("decryption with wrong epoch should fail or produce garbage")
}

Expand All @@ -133,7 +131,7 @@ func (sc *kmKeyGenerationImpl) Run(childEnv *env.Env) error {
"wrong key pair id",
ciphertext,
)
if err == nil && decrypted == plaintext {
if err == nil && reflect.DeepEqual(decrypted, plaintext) {
return fmt.Errorf("decryption with wrong key pair id should fail or produce garbage")
}

Expand Down Expand Up @@ -174,6 +172,9 @@ func (sc *kmKeyGenerationImpl) Run(childEnv *env.Env) error {
return fmt.Errorf("decryption with invalid epoch should fail")
}

// Check the logs whether any issues were detected.
sc.Net.CheckLogWatchers()

return nil
}

Expand All @@ -183,24 +184,24 @@ func (sc *kmKeyGenerationImpl) submitKeyValueRuntimeEncryptTx(
nonce uint64,
epoch beacon.EpochTime,
keyPairID string,
plaintext string,
) (string, error) {
plaintext []byte,
) ([]byte, error) {
rawRsp, err := sc.submitRuntimeTx(ctx, runtimeID, nonce, "encrypt", struct {
Epoch uint64 `json:"epoch"`
KeyPairID string `json:"key_pair_id"`
Plaintext string `json:"plaintext"`
Plaintext []byte `json:"plaintext"`
}{
Epoch: uint64(epoch),
KeyPairID: keyPairID,
Plaintext: plaintext,
})
if err != nil {
return "", fmt.Errorf("failed to submit encrypt tx to runtime: %w", err)
return nil, fmt.Errorf("failed to submit encrypt tx to runtime: %w", err)
}

var rsp string
var rsp []byte
if err = cbor.Unmarshal(rawRsp, &rsp); err != nil {
return "", fmt.Errorf("failed to unmarshal response from runtime: %w", err)
return nil, fmt.Errorf("failed to unmarshal response from runtime: %w", err)
}

return rsp, nil
Expand All @@ -212,24 +213,24 @@ func (sc *kmKeyGenerationImpl) submitKeyValueRuntimeDecryptTx(
nonce uint64,
epoch beacon.EpochTime,
keyPairID string,
ciphertext string,
) (string, error) {
ciphertext []byte,
) ([]byte, error) {
rawRsp, err := sc.submitRuntimeTx(ctx, runtimeID, nonce, "decrypt", struct {
Epoch uint64 `json:"epoch"`
KeyPairID string `json:"key_pair_id"`
Ciphertext string `json:"ciphertext"`
Ciphertext []byte `json:"ciphertext"`
}{
Epoch: uint64(epoch),
KeyPairID: keyPairID,
Ciphertext: ciphertext,
})
if err != nil {
return "", fmt.Errorf("failed to submit decrypt tx to runtime: %w", err)
return nil, fmt.Errorf("failed to submit decrypt tx to runtime: %w", err)
}

var rsp string
var rsp []byte
if err = cbor.Unmarshal(rawRsp, &rsp); err != nil {
return "", fmt.Errorf("failed to unmarshal response from runtime: %w", err)
return nil, fmt.Errorf("failed to unmarshal response from runtime: %w", err)
}

return rsp, nil
Expand Down
2 changes: 0 additions & 2 deletions tests/runtimes/simple-keyvalue/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@ anyhow = "1.0"
thiserror = "1.0"
io-context = "0.2.0"
byteorder = "1.4.3"
rand = "0.7.3"
base64 = "0.13.0"
x25519-dalek = "1.1.0"
tokio = { version = "1", features = ["rt"] }

Expand Down
42 changes: 21 additions & 21 deletions tests/runtimes/simple-keyvalue/src/methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@
use std::{collections::BTreeMap, convert::TryInto};

use base64;
use io_context::Context as IoContext;
use rand::rngs::OsRng;
use x25519_dalek;

use super::{crypto::EncryptionContext, types::*, Context, TxContext};
Expand Down Expand Up @@ -358,13 +356,14 @@ impl Methods {
}

/// ElGamal encryption.
pub fn encrypt(ctx: &mut TxContext, args: Encrypt) -> Result<Option<String>, String> {
pub fn encrypt(ctx: &mut TxContext, args: Encrypt) -> Result<Option<Vec<u8>>, String> {
if ctx.is_check_only() {
return Ok(None);
}

// Derive key pair ID based on the given ID.
let key_pair_id = KeyPairId::from(Hash::digest_bytes(args.key_pair_id.as_bytes()).as_ref());
let hash = Hash::digest_bytes(args.key_pair_id.as_bytes()).0;
let key_pair_id = KeyPairId::from(hash.as_ref());

// Fetch public key.
let io_ctx = IoContext::create_child(&ctx.parent.core.io_ctx);
Expand All @@ -377,37 +376,36 @@ impl Methods {
.map_err(|err| err.to_string())?
.ok_or("public ephemeral key not available")?;

// Generate ephemeral key.
let mut rng = OsRng {};
let ephemeral_sk = x25519_dalek::StaticSecret::new(&mut rng);
// Generate ephemeral key. Not secure, but good enough for testing purposes.
let ephemeral_sk = x25519_dalek::StaticSecret::from(hash);
let ephemeral_pk = x25519_dalek::PublicKey::from(&ephemeral_sk);

// ElGamal encryption.
let ciphertext = deoxysii::box_seal(
&[0u8; NONCE_SIZE],
args.plaintext.into_bytes(),
args.plaintext,
vec![],
&long_term_pk.key.0,
&ephemeral_sk.to_bytes(),
)
.map_err(|err| format!("failed to encrypt plaintext: {}", err))?;

// Encode ephemeral_pk || ciphertext to Base64.
let mut v = ephemeral_pk.as_bytes().to_vec();
v.extend(ciphertext);
let ciphertext = base64::encode(v);
// Return ephemeral_pk || ciphertext.
let mut c = ephemeral_pk.as_bytes().to_vec();
c.extend(ciphertext);

Ok(Some(ciphertext))
Ok(Some(c))
}

/// ElGamal decryption.
pub fn decrypt(ctx: &mut TxContext, args: Decrypt) -> Result<Option<String>, String> {
pub fn decrypt(ctx: &mut TxContext, args: Decrypt) -> Result<Option<Vec<u8>>, String> {
if ctx.is_check_only() {
return Ok(None);
}

// Derive key pair ID based on the given ID.
let key_pair_id = KeyPairId::from(Hash::digest_bytes(args.key_pair_id.as_bytes()).as_ref());
let hash = Hash::digest_bytes(args.key_pair_id.as_bytes()).0;
let key_pair_id = KeyPairId::from(hash.as_ref());

// Fetch private key.
let io_ctx = IoContext::create_child(&ctx.parent.core.io_ctx);
Expand All @@ -419,14 +417,18 @@ impl Methods {
.block_on(result)
.map_err(|err| format!("private ephemeral key not available: {}", err))?;

// Decode ephemeral_pk || ciphertext from Base64.
let ciphertext = base64::decode(args.ciphertext).map_err(|err| err.to_string())?;
let ephemeral_pk = ciphertext
// Decode ephemeral_pk || ciphertext.
let ephemeral_pk = args
.ciphertext
.get(0..32)
.ok_or("invalid ciphertext")?
.try_into()
.unwrap();
let ciphertext = ciphertext.get(32..).ok_or("invalid ciphertext")?.to_vec();
let ciphertext = args
.ciphertext
.get(32..)
.ok_or("invalid ciphertext")?
.to_vec();

// ElGamal decryption.
let plaintext = deoxysii::box_open(
Expand All @@ -436,8 +438,6 @@ impl Methods {
ephemeral_pk,
&long_term_sk.input_keypair.sk.0,
)
.map(String::from_utf8)
.map_err(|err| format!("failed to decrypt ciphertext: {}", err))?
.map_err(|err| format!("failed to decrypt ciphertext: {}", err))?;

Ok(Some(plaintext))
Expand Down
4 changes: 2 additions & 2 deletions tests/runtimes/simple-keyvalue/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,14 @@ pub struct KeyValue {
pub struct Encrypt {
pub epoch: u64,
pub key_pair_id: String,
pub plaintext: String,
pub plaintext: Vec<u8>,
}

#[derive(Clone, Debug, Default, cbor::Encode, cbor::Decode)]
pub struct Decrypt {
pub epoch: u64,
pub key_pair_id: String,
pub ciphertext: String,
pub ciphertext: Vec<u8>,
}

#[derive(Clone, Debug, Default, cbor::Encode, cbor::Decode)]
Expand Down

0 comments on commit a50d16b

Please sign in to comment.