Secret/public key wrapper guidelines #145
Unanswered
semenov-vladyslav
asked this question in
General
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
SecretKey/PublicKey wrappers guidelines
This is a guideline to implementing wrappers for secret/public key types. It defines the following:
Conversion/serialization methods naming/typing rules
try_
-- prefix used with methods that can fail, usuallytry_from_xxx
; must returnResult<T>
;to_
/from_
/as_
-- reflects operation;to_
returns non-ref type,as_
returns ref type; must not returnResult<T>
;bytes
/ref
/slice
-- reflects operand type:bytes
methods must accept or return[u8; N]
type;ref
methods must accept or return&[u8; N]
type;slice
methods must accept or return&[u8]
orT: AsRef<[u8]>
type;&[u8; N]
should be preferred to&[u8]
as it retains accepted array length information.Secret keys
Constants
pub const SECRET_KEY_LEN: usize
-- length in raw/serialized bytes.Recommended traits
Zeroize
-- if internal/wrapped type does not implement it already;Drop
-- perform zeroize on drop, can be derived with#[zeroize(drop)]
;Into
/TryInto
,From
/TryFrom
-- conversions to/from bytes:impl From<[u8; SECRET_KEY_LEN]> for SecretKey
(orimpl From<&[u8; SECRET_KEY_LEN]> for SecretKey
?) orTryFrom
if secret key bytes need verification that can fail -- conversion from raw/serialized secret bytes;impl TryFrom<&[u8]> for SecretKey
-- conversion from a slice, can fail as slice can have bad length;impl From<SecretKey> for [u8; SECRET_KEY_LEN]
-- conversion to a serialized byte array;Not-recommended traits
Clone
-- secret keys should really not be cloned/copied, a clone/copy can be reused/misused which might lead to security threat. Instead, new keys should be "derived" from master-key. In this case key system becomes non-trivial and should be documented. One example of such key system isslip10
.Copy
-- can't be implemented without implementingClone
trait.Debug
-- makes little sense and only in test/debug builds;PartialEq
/Eq
/Ord
-- secret keys may not have a reasonable equality, there's no reasonable use-case where secret key equality comparison is justified. DefaultPartialEq
may not be safe (non-constant time, might lead to side-channel attacks), it's advised to use constant time equality comparison fromsubtle
crate.Hash
-- secret keys should not be used as keys in associative containers. DefaultHash
instance might leak information about secret key bits. If theHash
instance is justified it should be cryptographically secure: key derivation of MAC mechanism should be used.AsRef<[u8]>
-- access to raw/serialized secret key bytes (not internal representation as scalar bytes) viaas_ref
is discouraged to minimize API;signature::Signer
-- signing operation, discouraged to avoid additional dependency.Secret key specific methods
pub fn generate() -> Result<Self>
-- generate a new random key with built-in rng;pub fn generate_with<R: RngCore + CryptoRng>(rng: &mut R) -> Self
-- generate a new random key with provided rng;pub fn public_key(&self) -> PublicKey
-- get public key;Serialization/conversion methods
pub fn to_bytes(&self) -> [u8; SECRET_KEY_LEN]
discouraged;pub fn as_bytes(&self) -> &[u8; SECRET_KEY_LEN]
orpub fn as_slice(&self) -> &[u8]
;pub fn from_bytes(bytes: [u8; SECRET_KEY_LEN]) -> Self
orpub fn try_from_bytes(bytes: [u8; SECRET_KEY_LEN]) -> Result<Self>
;pub fn try_from_slice(slice: &[u8]) -> Result<Self>
orpub fn try_from_slice<T: AsRef<[u8]>>(slice: T) -> Result<Self>
;Security
Secret key bytes must not leak. To ensure that many implementations implement
Zeroize
trait. However, fFunction arguments, result values, and local arrays might leak secret key bytes:fn from_slice(slice: &[u8]) -> SecretKey
does not guarantee that the caller will clearslice
;fn from_bytes(bytes: [u8; SECRET_KEY_LEN]) -> SecretKey
does not guarantee that the compiler will actually move (and will not copybytes
), or clearbytes
after it's been used, or won't remove such cleanup;fn generate<R>(rng: &mut R) -> SecretKey
implemented vialet mut bytes = [0_u8; SECRET_KEY_LEN]
does not guarantee that localbytes
will be cleared;fn to_bytes(&self) -> [u8; SECRET_KEY_LEN]
does not guarantee that the caller will clear result array;Example 1:
This implementation leaks secret key bytes in release build (ie. compiler can't optimize away copying).
Example 2:
Here compiler does perform move.
Example 3:
crypto::signatures::ed25519::SecretKey::generate()
(built in release mode) leaves behind 4 additional copies of the secret key in stack besides the secret key itself.ed25519_zebra::SigningKey
does not performzeroize
when dropping secret key.Extent of the problem is not clear: whether it's possible or not to exploit such leaks. The common recommendation is to keep only one copy (including implicitly created by compiler) of a secret key.
Solution 0: Do nothing and claim it's not a potential vulnerability (not true, it is!).
Solution 1: Replace secret bytes type
[u8; N]
with#[derive(Zeroize)] pub struct SecretBytes<const N: usize>(pub [u8; N])
in function arguments, local functions, result types. Obviously, this is a breaking and inconvenient change.Solution 2: Make array cleanup responsibility of the caller. Encourage use of
.zeroize()
, make code review/audit of potentially hasardous code. Not clear how to go about function arguments and return values:Solution 3: Forbid use of
[u8; N]
type for secret bytes in function arguments and return values. Make array cleanup responsibility of the caller. Encourage use of.zeroize()
, make code review/audit of potentially hasardous code:Public keys
Constants
pub const PUBLIC_KEY_LEN: usize
-- length in raw/serialized bytes.Traits
Into
/TryInto
,From
/TryFrom
-- conversions to/from bytes:impl TryFrom<[u8; PUBLIC_KEY_LEN]> for PublicKey
(orimpl TryFrom<&[u8; PUBLIC_KEY_LEN]> for PublicKey
?) public key must be verified, not all bytestring represent a valid public key -- conversion from raw/serialized bytes;impl TryFrom<&[u8]> for PublicKey
-- conversion from a slice, can fail as slice can have bad length;impl From<PublicKey> for [u8; SECRET_KEY_LEN]
-- conversion to a serialized byte array;Clone
,Copy
,Debug
,PartialEq
/Eq
/Ord
,Hash
,AsRef<[u8]>
-- the usual interoperability traits;signature::Verifier
-- discouraged in order to minimized dependencies.Security
Additional types
Definitions of additional types such as
Signature
,SharedKey
, etc. is outside of scope of the current guide.Template
Beta Was this translation helpful? Give feedback.
All reactions