Skip to content

Commit

Permalink
feat: private authwit with static call (#7073)
Browse files Browse the repository at this point in the history
Updates the private authwit to use static calls instead. Without this,
it is possible to re-enter using the authwits which is horrible.

---

The private authwit validation is now making a static call to the
account contract instead of passing over control flow. This is to ensure
that it cannot be used for re-entry.

To make this change however, we cannot allow emitting a nullifying from
the account contract, since that would break the static call. Instead,
we will be changing the `verify_private_authwit` to a
`verify_private_authwit` and in the `auth` library emit the nullifier.
This means that the "calling" contract will now be emitting the
nullifier, and not the account. For example, for a token contract, the
nullifier is now emitted by the token contract. However, as this is done
inside the `auth` library, the token contract don't need to change much.

The biggest difference is related to "cancelling" an authwit. Since it
is no longer in the account contract, you cannot just emit a nullifier
from it anymore. Instead it must rely on the token contract providing
functionality for cancelling.

There are also a few general changes to how authwits are generated,
namely to more easily support the data required for a validity lookup
now. Previously we could lookup the `message_hash` directly at the
account contract, now we instead need to use the `inner_hash` and the
contract of the consumer to figure out if it have already been emitted.

A minor extension have been made to the authwit creations to make it
easier specific a hash that needs to be signed with a specific caller,
e.g., the `inner_hash` can be provided as `{consumer, inner_hash}` to
the `createAuthWit` where it previously needed to do a couple of manual
steps to compute the outer hash. The `computeOuterAuthWitHash` have been
amde internal and the `computeAuthWitMessageHash` can instead be used to
compute the values similarly to other authwit computations.

```diff
const innerHash = computeInnerAuthWitHash([Fr.ZERO, functionSelector.toField(), entrypointPackedArgs.hash]);
-const outerHash = computeOuterAuthWitHash(
-    this.dappEntrypointAddress,
-    new Fr(this.chainId),
-    new Fr(this.version),
-    innerHash,
-);
+const outerHash = computeAuthWitMessageHash(
+    { consumer: this.dappEntrypointAddress, innerHash },
+    { chainId: new Fr(this.chainId), version: new Fr(this.version) },
+);
```

If the wallet is used to compute the authwit, it will populate the chain
id and version instead of requiring it to be provided by tha actor.

```diff
const innerHash = computeInnerAuthWitHash([Fr.fromString('0xdead')]);
-const outerHash = computeOuterAuthWitHash(wallets[1].getAddress(), chainId, version, innerHash);
-const witness = await wallets[0].createAuthWit(outerHash);
+ const witness = await wallets[0].createAuthWit({ comsumer: accounts[1].address, inner_hash });
```
  • Loading branch information
LHerskind authored Jun 21, 2024
1 parent 13678be commit 9c52d47
Show file tree
Hide file tree
Showing 56 changed files with 672 additions and 664 deletions.
8 changes: 3 additions & 5 deletions docs/docs/guides/smart_contracts/writing_contracts/authwit.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ As part of `AuthWit` we are assuming that the `on_behalf_of` implements the priv

```rust
#[aztec(private)]
fn spend_private_authwit(inner_hash: Field) -> Field;
fn verify_private_authwit(inner_hash: Field) -> Field;
```

For public authwit, we have a shared registry that is used, there we are using a `consume` function.
Expand All @@ -101,10 +101,8 @@ To make it convenient to compute the message hashes in TypeScript, the `aztec.js

For private calls where we allow execution on behalf of others, we generally want to check if the current call is authenticated by `on_behalf_of`. To easily do so, we can use the `assert_current_call_valid_authwit` which fetches information from the current context without us needing to provide much beyond the `on_behalf_of`.

This function will then make a to `on_behalf_of` to execute the `spend_private_authwit` function which validates that the call is authenticated.
The `on_behalf_of` should assert that we are indeed authenticated and then emit a nullifier when we are spending the authwit to prevent replay attacks.
If the return value is not as expected, we throw an error.
This is to cover the case where the `on_behalf_of` might implemented some function with the same selector as the `spend_private_authwit` that could be used to authenticate unintentionally.
This function will then make a call to `on_behalf_of` to execute the `verify_private_authwit` function which validates that the call is authenticated.
The `on_behalf_of` should assert that we are indeed authenticated and then return the `IS_VALID` selector. If the return value is not as expected, we throw an error. This is to cover the case where the `on_behalf_of` might implemented some function with the same selector as the `verify_private_authwit` that could be used to authenticate unintentionally.

#### Example

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ E.g. you don't want a user to subscribe once they have subscribed already. Or yo

Emit a nullifier in your function. By adding this nullifier into the tree, you prevent another nullifier from being added again. This is also why in authwit, we emit a nullifier, to prevent someone from reusing their approval.

#include_code spend_private_authwit /noir-projects/aztec-nr/authwit/src/account.nr rust
#include_code verify_private_authwit /noir-projects/aztec-nr/authwit/src/account.nr rust

Note be careful to ensure that the nullifier is not deterministic and that no one could do a preimage analysis attack. More in [the anti pattern section on deterministic nullifiers](#deterministic-nullifiers)

Expand Down
44 changes: 40 additions & 4 deletions docs/docs/migration_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ Aztec is in full-speed development. Literally every version breaks compatibility
## TBD

### [Aztec.nr] changes to `NoteInterface`

`compute_nullifier` function was renamed to `compute_note_hash_and_nullifier` and now the function has to return not only the nullifier but also the note hash used to compute the nullifier.
The same change was done to `compute_nullifier_without_context` function.
These changes were done because having the note hash exposed allowed us to not having to re-compute it again in `destroy_note` function of Aztec.nr which led to significant decrease in gate counts (see the [optimization PR](https://github.com/AztecProtocol/aztec-packages/pull/7103) for more details).


```diff
- impl NoteInterface<VALUE_NOTE_LEN, VALUE_NOTE_BYTES_LEN> for ValueNote {
- fn compute_nullifier(self, context: &mut PrivateContext) -> Field {
Expand Down Expand Up @@ -89,10 +89,46 @@ To further reduce gate count, you can iterate over `options.limit` instead of `m
+ for i in 0..options.limit {
```

### [Aztec.nr] static private authwit

The private authwit validation is now making a static call to the account contract instead of passing over control flow. This is to ensure that it cannot be used for re-entry.

To make this change however, we cannot allow emitting a nullifier from the account contract, since that would break the static call. Instead, we will be changing the `spend_private_authwit` to a `verify_private_authwit` and in the `auth` library emit the nullifier. This means that the "calling" contract will now be emitting the nullifier, and not the account. For example, for a token contract, the nullifier is now emitted by the token contract. However, as this is done inside the `auth` library, the token contract doesn't need to change much.

The biggest difference is related to "cancelling" an authwit. Since it is no longer in the account contract, you cannot just emit a nullifier from it anymore. Instead it must rely on the token contract providing functionality for cancelling.

There are also a few general changes to how authwits are generated, namely to more easily support the data required for a validity lookup now. Previously we could lookup the `message_hash` directly at the account contract, now we instead need to use the `inner_hash` and the contract of the consumer to figure out if it have already been emitted.

A minor extension have been made to the authwit creations to make it easier to sign a specific a hash with a specific caller, e.g., the `inner_hash` can be provided as `{consumer, inner_hash}` to the `createAuthWit` where it previously needed to do a couple of manual steps to compute the outer hash. The `computeOuterAuthWitHash` have been amde internal and the `computeAuthWitMessageHash` can instead be used to compute the values similarly to other authwit computations.

```diff
const innerHash = computeInnerAuthWitHash([Fr.ZERO, functionSelector.toField(), entrypointPackedArgs.hash]);
-const outerHash = computeOuterAuthWitHash(
- this.dappEntrypointAddress,
- new Fr(this.chainId),
- new Fr(this.version),
- innerHash,
-);
+const outerHash = computeAuthWitMessageHash(
+ { consumer: this.dappEntrypointAddress, innerHash },
+ { chainId: new Fr(this.chainId), version: new Fr(this.version) },
+);
```

If the wallet is used to compute the authwit, it will populate the chain id and version instead of requiring it to be provided by tha actor.

```diff
const innerHash = computeInnerAuthWitHash([Fr.fromString('0xdead')]);
-const outerHash = computeOuterAuthWitHash(wallets[1].getAddress(), chainId, version, innerHash);
-const witness = await wallets[0].createAuthWit(outerHash);
+ const witness = await wallets[0].createAuthWit({ comsumer: accounts[1].address, inner_hash });
```

## 0.43.0

### [Aztec.nr] break `token.transfer()` into `transfer` and `transferFrom`
Earlier we had just one function - `transfer()` which used authwits to handle the case where a contract/user wants to transfer funds on behalf of another user.

Earlier we had just one function - `transfer()` which used authwits to handle the case where a contract/user wants to transfer funds on behalf of another user.
To reduce circuit sizes and proof times, we are breaking up `transfer` and introducing a dedicated `transferFrom()` function like in the ERC20 standard.

### [Aztec.nr] `options.limit` has to be constant
Expand Down Expand Up @@ -909,9 +945,9 @@ After:

```rust
#[aztec(private)]
fn spend_private_authwit(inner_hash: Field) -> Field {
fn verify_private_authwit(inner_hash: Field) -> Field {
let actions = AccountActions::private(&mut context, ACCOUNT_ACTIONS_STORAGE_SLOT, is_valid_impl);
actions.spend_private_authwit(inner_hash)
actions.verify_private_authwit(inner_hash)
}

#[aztec(public)]
Expand Down
7 changes: 3 additions & 4 deletions noir-projects/aztec-nr/authwit/src/account.nr
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ impl AccountActions<&mut PrivateContext> {
}
// docs:end:entrypoint

// docs:start:spend_private_authwit
pub fn spend_private_authwit(self, inner_hash: Field) -> Field {
// docs:start:verify_private_authwit
pub fn verify_private_authwit(self, inner_hash: Field) -> Field {
// The `inner_hash` is "siloed" with the `msg_sender` to ensure that only it can
// consume the message.
// This ensures that contracts cannot consume messages that are not intended for them.
Expand All @@ -44,8 +44,7 @@ impl AccountActions<&mut PrivateContext> {
);
let valid_fn = self.is_valid_impl;
assert(valid_fn(self.context, message_hash) == true, "Message not authorized by account");
self.context.push_new_nullifier(message_hash, 0);
IS_VALID_SELECTOR
}
// docs:end:spend_private_authwit
// docs:end:verify_private_authwit
}
35 changes: 30 additions & 5 deletions noir-projects/aztec-nr/authwit/src/auth.nr
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use dep::aztec::protocol_types::{
abis::function_selector::FunctionSelector, address::AztecAddress,
constants::{GENERATOR_INDEX__AUTHWIT_INNER, GENERATOR_INDEX__AUTHWIT_OUTER, CANONICAL_AUTH_REGISTRY_ADDRESS},
constants::{
GENERATOR_INDEX__AUTHWIT_INNER, GENERATOR_INDEX__AUTHWIT_OUTER, GENERATOR_INDEX__AUTHWIT_NULLIFIER,
CANONICAL_AUTH_REGISTRY_ADDRESS
},
hash::pedersen_hash
};
use dep::aztec::{prelude::Deserialize, context::{PrivateContext, PublicContext, gas::GasOpts}, hash::hash_args_array};
Expand All @@ -10,20 +13,36 @@ global IS_VALID_SELECTOR = 0xabf64ad4; // 4 first bytes of keccak256("IS_VALID()
// docs:start:assert_current_call_valid_authwit
// Assert that `on_behalf_of` have authorized the current call with a valid authentication witness
pub fn assert_current_call_valid_authwit(context: &mut PrivateContext, on_behalf_of: AztecAddress) {
let function_selector = FunctionSelector::from_signature("spend_private_authwit(Field)");
let inner_hash = compute_inner_authwit_hash([context.msg_sender().to_field(), context.selector().to_field(), context.args_hash]);
let result: Field = context.call_private_function(on_behalf_of, function_selector, [inner_hash]).unpack_into();
assert(result == IS_VALID_SELECTOR, "Message not authorized by account");
assert_inner_hash_valid_authwit(context, on_behalf_of, inner_hash);
}
// docs:end:assert_current_call_valid_authwit

pub fn assert_inner_hash_valid_authwit(context: &mut PrivateContext, on_behalf_of: AztecAddress, inner_hash: Field) {
// We perform a static call here and not a standard one to ensure that the account contract cannot re-enter.
let result: Field = context.static_call_private_function(
on_behalf_of,
FunctionSelector::from_signature("verify_private_authwit(Field)"),
[inner_hash]
).unpack_into();
assert(result == IS_VALID_SELECTOR, "Message not authorized by account");
// Compute the nullifier, similar computation to the outer hash, but without the chain_id and version.
// Those should already be handled in the verification, so we just need something to nullify, that allow same inner_hash for multiple actors.
let nullifier = compute_authwit_nullifier(on_behalf_of, inner_hash);
context.push_new_nullifier(nullifier, 0);
}

// docs:start:assert_current_call_valid_authwit_public
// Assert that `on_behalf_of` have authorized the current call in a public context
pub fn assert_current_call_valid_authwit_public(context: &mut PublicContext, on_behalf_of: AztecAddress) {
let inner_hash = compute_inner_authwit_hash(
[(*context).msg_sender().to_field(), (*context).selector().to_field(), (*context).get_args_hash()]
);
assert_inner_hash_valid_authwit_public(context, on_behalf_of, inner_hash);
}
// docs:end:assert_current_call_valid_authwit_public

pub fn assert_inner_hash_valid_authwit_public(context: &mut PublicContext, on_behalf_of: AztecAddress, inner_hash: Field) {
let result: Field = context.call_public_function(
AztecAddress::from_field(CANONICAL_AUTH_REGISTRY_ADDRESS),
FunctionSelector::from_signature("consume((Field),Field)"),
Expand All @@ -32,7 +51,6 @@ pub fn assert_current_call_valid_authwit_public(context: &mut PublicContext, on_
).deserialize_into();
assert(result == IS_VALID_SELECTOR, "Message not authorized by account");
}
// docs:end:assert_current_call_valid_authwit_public

// docs:start:compute_call_authwit_hash
// Compute the message hash to be used by an authentication witness
Expand All @@ -54,6 +72,13 @@ pub fn compute_inner_authwit_hash<N>(args: [Field; N]) -> Field {
pedersen_hash(args, GENERATOR_INDEX__AUTHWIT_INNER)
}

pub fn compute_authwit_nullifier(on_behalf_of: AztecAddress, inner_hash: Field) -> Field {
pedersen_hash(
[on_behalf_of.to_field(), inner_hash],
GENERATOR_INDEX__AUTHWIT_NULLIFIER
)
}

pub fn compute_outer_authwit_hash(
consumer: AztecAddress,
chain_id: Field,
Expand Down
22 changes: 20 additions & 2 deletions noir-projects/aztec-nr/aztec/src/context/unconstrained_context.nr
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ use dep::protocol_types::address::AztecAddress;

struct UnconstrainedContext {
block_number: u32,
contract_address: AztecAddress,
contract_address: AztecAddress,
version: Field,
chain_id: Field,
}

impl UnconstrainedContext {
Expand All @@ -13,7 +15,9 @@ impl UnconstrainedContext {
// available.
let block_number = block_number_oracle();
let contract_address = contract_address_oracle();
Self { block_number, contract_address }
let chain_id = chain_id_oracle();
let version = version_oracle();
Self { block_number, contract_address, version, chain_id }
}

fn block_number(self) -> u32 {
Expand All @@ -23,10 +27,24 @@ impl UnconstrainedContext {
fn this_address(self) -> AztecAddress {
self.contract_address
}

fn version(self) -> Field {
self.version
}

fn chain_id(self) -> Field {
self.chain_id
}
}

#[oracle(getContractAddress)]
unconstrained fn contract_address_oracle() -> AztecAddress {}

#[oracle(getBlockNumber)]
unconstrained fn block_number_oracle() -> u32 {}

#[oracle(getChainId)]
unconstrained fn chain_id_oracle() -> Field {}

#[oracle(getVersion)]
unconstrained fn version_oracle() -> Field {}
2 changes: 1 addition & 1 deletion noir-projects/aztec-nr/aztec/src/encrypted_logs/header.nr
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ fn test_encrypted_log_header() {
let ciphertext = header.compute_ciphertext(secret, point);

let expected_header_ciphertext = [
131, 119, 105, 129, 244, 32, 151, 205, 12, 99, 93, 62, 10, 180, 72, 21, 179, 36, 250, 95, 56, 167, 171, 16, 195, 164, 223, 57, 75, 5, 24, 119, 198, 34, 99, 189, 193, 183, 227, 43, 79, 204, 214, 89, 221, 153, 246, 64
228, 9, 65, 81, 62, 59, 249, 207, 90, 196, 206, 72, 39, 199, 82, 196, 23, 131, 32, 226, 26, 176, 43, 39, 239, 177, 177, 192, 85, 216, 17, 15, 18, 187, 35, 225, 135, 192, 63, 88, 29, 173, 232, 46, 72, 82, 187, 139
];

assert_eq(ciphertext, expected_header_ciphertext);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ mod test {
let ciphertext = body.compute_ciphertext(eph_sk, ivpk_app);

let expected_note_body_ciphertext = [
131, 119, 105, 129, 244, 32, 151, 205, 12, 99, 93, 62, 10, 180, 72, 21, 47, 232, 95, 17, 240, 230, 80, 129, 174, 158, 23, 76, 114, 185, 43, 18, 254, 148, 147, 230, 66, 216, 167, 62, 180, 213, 238, 33, 108, 29, 84, 139, 99, 206, 212, 253, 92, 116, 137, 31, 0, 104, 45, 91, 250, 109, 141, 114, 189, 53, 35, 60, 108, 156, 170, 206, 150, 114, 150, 187, 198, 13, 62, 153, 133, 13, 169, 167, 242, 221, 40, 168, 186, 203, 104, 82, 47, 238, 142, 179, 90, 37, 9, 70, 245, 176, 122, 247, 42, 87, 75, 7, 20, 89, 166, 123, 14, 26, 230, 156, 49, 94, 0, 94, 72, 58, 171, 239, 115, 174, 155, 7, 151, 17, 60, 206, 193, 134, 70, 87, 215, 88, 21, 194, 63, 26, 106, 105, 124, 213, 252, 152, 192, 71, 115, 13, 181, 5, 169, 15, 170, 196, 174, 228, 170, 192, 91, 76, 110, 220, 89, 47, 248, 144, 189, 251, 167, 149, 248, 226
228, 9, 65, 81, 62, 59, 249, 207, 90, 196, 206, 72, 39, 199, 82, 196, 63, 127, 188, 251, 150, 188, 238, 205, 3, 86, 102, 164, 175, 12, 137, 158, 163, 111, 205, 10, 229, 230, 46, 202, 110, 107, 156, 180, 67, 192, 161, 201, 48, 153, 169, 1, 25, 182, 93, 39, 39, 207, 251, 218, 234, 147, 156, 13, 110, 180, 190, 199, 41, 6, 211, 203, 176, 110, 165, 186, 110, 127, 199, 22, 201, 149, 92, 249, 219, 68, 145, 68, 179, 29, 233, 34, 98, 123, 197, 234, 169, 53, 44, 14, 81, 60, 92, 27, 250, 134, 49, 248, 57, 119, 236, 118, 158, 104, 82, 243, 98, 164, 60, 72, 74, 27, 177, 194, 221, 225, 193, 150, 67, 235, 205, 106, 150, 24, 126, 186, 220, 178, 199, 189, 113, 54, 181, 55, 46, 15, 236, 236, 9, 159, 5, 172, 237, 154, 110, 50, 241, 64, 92, 13, 37, 53, 20, 140, 42, 146, 229, 63, 97, 25, 159, 63, 235, 104, 68, 100
];

assert_eq(expected_note_body_ciphertext.len(), ciphertext.len());
Expand Down Expand Up @@ -206,7 +206,7 @@ mod test {
let ciphertext = body.compute_ciphertext(eph_sk, ivpk_app);

let expected_event_body_ciphertext = [
131, 119, 105, 129, 244, 32, 151, 205, 12, 99, 93, 62, 10, 180, 72, 21, 47, 232, 95, 17, 240, 230, 80, 129, 174, 158, 23, 76, 114, 185, 43, 18, 254, 148, 147, 230, 66, 216, 167, 62, 180, 213, 238, 33, 108, 29, 84, 139, 157, 165, 187, 138, 35, 3, 236, 75, 197, 105, 102, 247, 224, 253, 13, 217, 145, 62, 96, 167, 93, 23, 18, 198, 187, 91, 8, 3, 197, 195, 127, 9, 218, 111, 125, 97, 141, 129, 142, 1, 230, 108, 35, 211, 170, 170, 170, 249, 249, 104, 68, 191, 245, 207, 182, 245, 248, 82, 175, 83, 155, 138, 208, 65, 31, 129, 251, 242, 219, 76, 17, 61, 178, 187, 108, 114, 177, 215, 175, 189, 166, 221, 94, 9, 22, 57, 151, 204, 57, 220, 129, 243, 217, 18, 101, 128, 229, 40, 254, 175, 2, 21, 31, 198, 18, 152, 169, 32, 113, 92, 37, 65, 169, 119, 95, 149, 239, 8, 23, 182, 22, 209, 207, 120, 133, 90, 252, 106
228, 9, 65, 81, 62, 59, 249, 207, 90, 196, 206, 72, 39, 199, 82, 196, 63, 127, 188, 251, 150, 188, 238, 205, 3, 86, 102, 164, 175, 12, 137, 158, 163, 111, 205, 10, 229, 230, 46, 202, 110, 107, 156, 180, 67, 192, 161, 201, 66, 122, 29, 35, 42, 33, 153, 216, 199, 208, 103, 207, 126, 153, 189, 136, 19, 220, 238, 15, 169, 29, 255, 11, 123, 107, 70, 192, 53, 40, 36, 93, 187, 32, 123, 136, 104, 23, 229, 245, 152, 90, 84, 2, 136, 112, 42, 27, 82, 214, 104, 14, 250, 48, 199, 245, 88, 22, 200, 77, 38, 51, 127, 56, 138, 255, 16, 46, 179, 129, 215, 185, 185, 116, 148, 16, 133, 62, 56, 180, 10, 132, 109, 77, 206, 199, 21, 167, 7, 163, 171, 158, 244, 23, 18, 121, 108, 42, 107, 7, 48, 84, 212, 104, 39, 16, 109, 7, 108, 129, 60, 80, 112, 241, 223, 140, 186, 158, 38, 74, 230, 213, 159, 175, 142, 228, 128, 160
];

assert_eq(expected_event_body_ciphertext.len(), ciphertext.len());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ mod test {
let ciphertext = body.compute_ciphertext(sender_ovsk_app, eph_pk);

let expected_outgoing_body_ciphertext = [
126, 10, 214, 39, 130, 143, 96, 143, 79, 143, 22, 36, 55, 41, 234, 255, 226, 26, 138, 236, 91, 188, 204, 216, 172, 133, 134, 69, 161, 237, 134, 5, 75, 192, 10, 6, 229, 54, 194, 56, 103, 243, 57, 248, 147, 237, 4, 3, 39, 28, 226, 30, 237, 228, 212, 115, 246, 244, 105, 39, 129, 119, 126, 207, 176, 14, 75, 134, 241, 23, 2, 187, 239, 86, 47, 56, 239, 20, 92, 176, 70, 12, 219, 226, 150, 70, 192, 43, 125, 53, 230, 153, 135, 228, 210, 197, 76, 123, 185, 190, 61, 172, 29, 168, 241, 191, 205, 71, 136, 72, 52, 115, 232, 246, 87, 42, 50, 150, 134, 108, 225, 90, 191, 191, 182, 150, 124, 147, 78, 249, 144, 111, 122, 187, 187, 5, 249, 167, 186, 14, 228, 128, 158, 138, 55, 99, 228, 46, 219, 187, 248, 122, 70, 31, 39, 209, 127, 23, 244, 84, 14, 93, 86, 208, 155, 151, 238, 70, 63, 3, 137, 59, 206, 230, 4, 20
127, 84, 96, 176, 101, 107, 236, 57, 68, 8, 53, 202, 138, 74, 186, 54, 74, 193, 245, 7, 109, 59, 218, 33, 1, 31, 205, 225, 241, 209, 64, 222, 94, 245, 4, 150, 47, 241, 187, 64, 152, 20, 102, 158, 200, 217, 213, 82, 1, 240, 170, 185, 51, 80, 27, 109, 63, 231, 235, 120, 174, 44, 133, 248, 10, 97, 60, 40, 222, 190, 147, 76, 187, 48, 91, 206, 48, 106, 56, 118, 38, 127, 82, 4, 182, 188, 44, 224, 31, 129, 47, 107, 134, 252, 20, 25, 122, 191, 158, 69, 35, 255, 215, 171, 196, 45, 91, 184, 83, 80, 238, 201, 1, 233, 235, 159, 171, 130, 158, 64, 176, 165, 132, 30, 84, 81, 71, 195, 145, 47, 82, 247, 210, 192, 23, 4, 220, 90, 56, 109, 46, 105, 79, 251, 165, 141, 185, 233, 191, 118, 219, 153, 191, 162, 99, 238, 241, 249, 9, 74, 210, 241, 54, 28, 126, 226, 85, 235, 174, 75, 239, 207, 100, 184, 248, 194
];

for i in 0..expected_outgoing_body_ciphertext.len() {
Expand Down
Loading

0 comments on commit 9c52d47

Please sign in to comment.