Compare commits
No commits in common. "main" and "0.2.0" have entirely different histories.
|
@ -1,10 +0,0 @@
|
|||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: github-actions
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: daily
|
||||
timezone: Etc/UTC
|
||||
open-pull-requests-limit: 10
|
||||
labels:
|
||||
- "A-CI"
|
|
@ -1,88 +0,0 @@
|
|||
name: CI checks
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Test on ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macOS-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Run tests
|
||||
run: cargo test --all-features --verbose
|
||||
- name: Verify working directory is clean
|
||||
run: git diff --exit-code
|
||||
|
||||
build-latest:
|
||||
name: Latest build on ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macOS-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
id: toolchain
|
||||
- run: rustup override set ${{steps.toolchain.outputs.name}}
|
||||
- name: Remove lockfile to build with latest dependencies
|
||||
run: rm Cargo.lock
|
||||
- name: Build crate
|
||||
run: cargo build --all-targets --all-features --verbose
|
||||
- name: Verify working directory is clean (excluding lockfile)
|
||||
run: git diff --exit-code ':!Cargo.lock'
|
||||
|
||||
build-nodefault:
|
||||
name: Build target ${{ matrix.target }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
target:
|
||||
- wasm32-wasi
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Add target
|
||||
run: rustup target add ${{ matrix.target }}
|
||||
- name: Build crate
|
||||
run: cargo build --no-default-features --verbose --target ${{ matrix.target }}
|
||||
|
||||
bitrot:
|
||||
name: Bitrot check
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
# Build benchmarks to prevent bitrot
|
||||
- name: Build benchmarks
|
||||
run: cargo build --benches
|
||||
|
||||
clippy:
|
||||
name: Clippy (MSRV)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Run Clippy
|
||||
uses: auguwu/clippy-action@1.3.0
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
working-directory: ${{ inputs.target }}
|
||||
deny: warnings
|
||||
|
||||
doc-links:
|
||||
name: Intra-doc links
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: cargo fetch
|
||||
# Requires #![deny(rustdoc::broken_intra_doc_links)] in crate.
|
||||
- name: Check intra-doc links
|
||||
run: cargo doc --all-features --document-private-items
|
||||
|
||||
fmt:
|
||||
name: Rustfmt
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Check formatting
|
||||
run: cargo fmt -- --check
|
|
@ -1 +0,0 @@
|
|||
target
|
19
CHANGELOG.md
19
CHANGELOG.md
|
@ -7,25 +7,6 @@ and this library adheres to Rust's notion of
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
## [0.4.0] - 2023-06-06
|
||||
### Changed
|
||||
- The `esk` and `ephemeral_key` arguments have been removed from
|
||||
`Domain::parse_note_plaintext_without_memo_ovk`. It is therefore no longer
|
||||
necessary (or possible) to ensure that `ephemeral_key` is derived from `esk`
|
||||
and the diversifier within the note plaintext. We have analyzed the safety of
|
||||
this change in the context of callers within `zcash_note_encryption` and
|
||||
`orchard`. See https://github.com/zcash/librustzcash/pull/848 and the
|
||||
associated issue https://github.com/zcash/librustzcash/issues/802 for
|
||||
additional detail.
|
||||
|
||||
## [0.3.0] - 2023-03-22
|
||||
### Changed
|
||||
- The `recipient` parameter has been removed from `Domain::note_plaintext_bytes`.
|
||||
- The `recipient` parameter has been removed from `NoteEncryption::new`. Since
|
||||
the `Domain::Note` type is now expected to contain information about the
|
||||
recipient of the note, there is no longer any need to pass this information
|
||||
in via the encryption context.
|
||||
|
||||
## [0.2.0] - 2022-10-13
|
||||
### Added
|
||||
- `zcash_note_encryption::Domain`:
|
||||
|
|
|
@ -1,166 +0,0 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "aead"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0"
|
||||
dependencies = [
|
||||
"crypto-common",
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "chacha20"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cipher",
|
||||
"cpufeatures",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chacha20poly1305"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35"
|
||||
dependencies = [
|
||||
"aead",
|
||||
"chacha20",
|
||||
"cipher",
|
||||
"poly1305",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cipher"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
|
||||
dependencies = [
|
||||
"crypto-common",
|
||||
"inout",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inout"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.150"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
|
||||
|
||||
[[package]]
|
||||
name = "opaque-debug"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
|
||||
|
||||
[[package]]
|
||||
name = "poly1305"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf"
|
||||
dependencies = [
|
||||
"cpufeatures",
|
||||
"opaque-debug",
|
||||
"universal-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||
|
||||
[[package]]
|
||||
name = "universal-hash"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea"
|
||||
dependencies = [
|
||||
"crypto-common",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "zcash_note_encryption"
|
||||
version = "0.4.0"
|
||||
dependencies = [
|
||||
"chacha20",
|
||||
"chacha20poly1305",
|
||||
"cipher",
|
||||
"rand_core",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9"
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "zcash_note_encryption"
|
||||
description = "Note encryption for Zcash transactions"
|
||||
version = "0.4.0"
|
||||
version = "0.2.0"
|
||||
authors = [
|
||||
"Jack Grigg <jack@electriccoin.co>",
|
||||
"Kris Nuttycombe <kris@electriccoin.co>"
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
[toolchain]
|
||||
channel = "1.56.1"
|
||||
components = ["clippy", "rustfmt"]
|
65
src/lib.rs
65
src/lib.rs
|
@ -19,8 +19,6 @@
|
|||
#![deny(unsafe_code)]
|
||||
// TODO: #![deny(missing_docs)]
|
||||
|
||||
use core::fmt::{self, Write};
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
extern crate alloc;
|
||||
#[cfg(feature = "alloc")]
|
||||
|
@ -74,28 +72,9 @@ impl AsRef<[u8]> for OutgoingCipherKey {
|
|||
/// Newtype representing the byte encoding of an [`EphemeralPublicKey`].
|
||||
///
|
||||
/// [`EphemeralPublicKey`]: Domain::EphemeralPublicKey
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct EphemeralKeyBytes(pub [u8; 32]);
|
||||
|
||||
impl fmt::Debug for EphemeralKeyBytes {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
struct HexFmt<'b>(&'b [u8]);
|
||||
impl<'b> fmt::Debug for HexFmt<'b> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_char('"')?;
|
||||
for b in self.0 {
|
||||
f.write_fmt(format_args!("{:02x}", b))?;
|
||||
}
|
||||
f.write_char('"')
|
||||
}
|
||||
}
|
||||
|
||||
f.debug_tuple("EphemeralKeyBytes")
|
||||
.field(&HexFmt(&self.0))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for EphemeralKeyBytes {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
&self.0
|
||||
|
@ -192,7 +171,20 @@ pub trait Domain {
|
|||
fn kdf(secret: Self::SharedSecret, ephemeral_key: &EphemeralKeyBytes) -> Self::SymmetricKey;
|
||||
|
||||
/// Encodes the given `Note` and `Memo` as a note plaintext.
|
||||
fn note_plaintext_bytes(note: &Self::Note, memo: &Self::Memo) -> NotePlaintextBytes;
|
||||
///
|
||||
/// # Future breaking changes
|
||||
///
|
||||
/// The `recipient` argument is present as a secondary way to obtain the diversifier;
|
||||
/// this is due to a historical quirk of how the Sapling `Note` struct was implemented
|
||||
/// in the `zcash_primitives` crate. `recipient` will be removed from this method in a
|
||||
/// future crate release, once [`zcash_primitives` has been refactored].
|
||||
///
|
||||
/// [`zcash_primitives` has been refactored]: https://github.com/zcash/librustzcash/issues/454
|
||||
fn note_plaintext_bytes(
|
||||
note: &Self::Note,
|
||||
recipient: &Self::Recipient,
|
||||
memo: &Self::Memo,
|
||||
) -> NotePlaintextBytes;
|
||||
|
||||
/// Derives the [`OutgoingCipherKey`] for an encrypted note, given the note-specific
|
||||
/// public data and an `OutgoingViewingKey`.
|
||||
|
@ -250,6 +242,8 @@ pub trait Domain {
|
|||
/// which may be passed via `self`).
|
||||
/// - The note plaintext contains valid encodings of its various fields.
|
||||
/// - Any domain-specific requirements are satisfied.
|
||||
/// - `ephemeral_key` can be derived from `esk` and the diversifier within the note
|
||||
/// plaintext.
|
||||
///
|
||||
/// `&self` is passed here to enable the implementation to enforce contextual checks,
|
||||
/// such as rules like [ZIP 212] that become active at a specific block height.
|
||||
|
@ -258,6 +252,8 @@ pub trait Domain {
|
|||
fn parse_note_plaintext_without_memo_ovk(
|
||||
&self,
|
||||
pk_d: &Self::DiversifiedTransmissionKey,
|
||||
esk: &Self::EphemeralSecretKey,
|
||||
ephemeral_key: &EphemeralKeyBytes,
|
||||
plaintext: &NotePlaintextBytes,
|
||||
) -> Option<(Self::Note, Self::Recipient)>;
|
||||
|
||||
|
@ -353,6 +349,7 @@ pub struct NoteEncryption<D: Domain> {
|
|||
epk: D::EphemeralPublicKey,
|
||||
esk: D::EphemeralSecretKey,
|
||||
note: D::Note,
|
||||
to: D::Recipient,
|
||||
memo: D::Memo,
|
||||
/// `None` represents the `ovk = ⊥` case.
|
||||
ovk: Option<D::OutgoingViewingKey>,
|
||||
|
@ -361,12 +358,18 @@ pub struct NoteEncryption<D: Domain> {
|
|||
impl<D: Domain> NoteEncryption<D> {
|
||||
/// Construct a new note encryption context for the specified note,
|
||||
/// recipient, and memo.
|
||||
pub fn new(ovk: Option<D::OutgoingViewingKey>, note: D::Note, memo: D::Memo) -> Self {
|
||||
pub fn new(
|
||||
ovk: Option<D::OutgoingViewingKey>,
|
||||
note: D::Note,
|
||||
to: D::Recipient,
|
||||
memo: D::Memo,
|
||||
) -> Self {
|
||||
let esk = D::derive_esk(¬e).expect("ZIP 212 is active.");
|
||||
NoteEncryption {
|
||||
epk: D::ka_derive_public(¬e, &esk),
|
||||
esk,
|
||||
note,
|
||||
to,
|
||||
memo,
|
||||
ovk,
|
||||
}
|
||||
|
@ -381,12 +384,14 @@ impl<D: Domain> NoteEncryption<D> {
|
|||
esk: D::EphemeralSecretKey,
|
||||
ovk: Option<D::OutgoingViewingKey>,
|
||||
note: D::Note,
|
||||
to: D::Recipient,
|
||||
memo: D::Memo,
|
||||
) -> Self {
|
||||
NoteEncryption {
|
||||
epk: D::ka_derive_public(¬e, &esk),
|
||||
esk,
|
||||
note,
|
||||
to,
|
||||
memo,
|
||||
ovk,
|
||||
}
|
||||
|
@ -407,7 +412,7 @@ impl<D: Domain> NoteEncryption<D> {
|
|||
let pk_d = D::get_pk_d(&self.note);
|
||||
let shared_secret = D::ka_agree_enc(&self.esk, &pk_d);
|
||||
let key = D::kdf(shared_secret, &D::epk_bytes(&self.epk));
|
||||
let input = D::note_plaintext_bytes(&self.note, &self.memo);
|
||||
let input = D::note_plaintext_bytes(&self.note, &self.to, &self.memo);
|
||||
|
||||
let mut output = [0u8; ENC_CIPHERTEXT_SIZE];
|
||||
output[..NOTE_PLAINTEXT_SIZE].copy_from_slice(&input.0);
|
||||
|
@ -534,8 +539,6 @@ fn check_note_validity<D: Domain>(
|
|||
cmstar_bytes: &D::ExtractedCommitmentBytes,
|
||||
) -> NoteValidity {
|
||||
if &D::ExtractedCommitmentBytes::from(&D::cmstar(note)) == cmstar_bytes {
|
||||
// In the case corresponding to specification section 4.19.3, we check that `esk` is equal
|
||||
// to `D::derive_esk(note)` prior to calling this method.
|
||||
if let Some(derived_esk) = D::derive_esk(note) {
|
||||
if D::epk_bytes(&D::ka_derive_public(note, &derived_esk))
|
||||
.ct_eq(ephemeral_key)
|
||||
|
@ -674,12 +677,12 @@ pub fn try_output_recovery_with_ock<D: Domain, Output: ShieldedOutput<D, ENC_CIP
|
|||
)
|
||||
.ok()?;
|
||||
|
||||
let (note, to) = domain.parse_note_plaintext_without_memo_ovk(&pk_d, &plaintext)?;
|
||||
let (note, to) =
|
||||
domain.parse_note_plaintext_without_memo_ovk(&pk_d, &esk, &ephemeral_key, &plaintext)?;
|
||||
let memo = domain.extract_memo(&plaintext);
|
||||
|
||||
// ZIP 212: Check that the esk provided to this function is consistent with the esk we can
|
||||
// derive from the note. This check corresponds to `ToScalar(PRF^{expand}_{rseed}([4]) = esk`
|
||||
// in https://zips.z.cash/protocol/protocol.pdf#decryptovk. (`ρ^opt = []` for Sapling.)
|
||||
// ZIP 212: Check that the esk provided to this function is consistent with the esk we
|
||||
// can derive from the note.
|
||||
if let Some(derived_esk) = D::derive_esk(¬e) {
|
||||
if (!derived_esk.ct_eq(&esk)).into() {
|
||||
return None;
|
||||
|
|
Loading…
Reference in New Issue