mirror of
https://github.com/fluencelabs/rust-libp2p
synced 2025-06-24 15:21:33 +00:00
core/: Add support for ECDSA identities (#2352)
Co-authored-by: Max Inden <mail@max-inden.de>
This commit is contained in:
@ -7,8 +7,11 @@
|
|||||||
|
|
||||||
- Migrate to Rust edition 2021 (see [PR 2339]).
|
- Migrate to Rust edition 2021 (see [PR 2339]).
|
||||||
|
|
||||||
|
- Add support for ECDSA identities (see [PR 2352]).
|
||||||
|
|
||||||
[PR 2339]: https://github.com/libp2p/rust-libp2p/pull/2339
|
[PR 2339]: https://github.com/libp2p/rust-libp2p/pull/2339
|
||||||
[PR 2350]: https://github.com/libp2p/rust-libp2p/pull/2350/
|
[PR 2350]: https://github.com/libp2p/rust-libp2p/pull/2350
|
||||||
|
[PR 2352]: https://github.com/libp2p/rust-libp2p/pull/2352
|
||||||
|
|
||||||
# 0.30.1 [2021-11-16]
|
# 0.30.1 [2021-11-16]
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ log = "0.4"
|
|||||||
multiaddr = { version = "0.13.0" }
|
multiaddr = { version = "0.13.0" }
|
||||||
multihash = { version = "0.14", default-features = false, features = ["std", "multihash-impl", "identity", "sha2"] }
|
multihash = { version = "0.14", default-features = false, features = ["std", "multihash-impl", "identity", "sha2"] }
|
||||||
multistream-select = { version = "0.11", path = "../misc/multistream-select" }
|
multistream-select = { version = "0.11", path = "../misc/multistream-select" }
|
||||||
|
p256 = { version = "0.9.0", default-features = false, features = ["ecdsa"], optional = true }
|
||||||
parking_lot = "0.11.0"
|
parking_lot = "0.11.0"
|
||||||
pin-project = "1.0.0"
|
pin-project = "1.0.0"
|
||||||
prost = "0.9"
|
prost = "0.9"
|
||||||
@ -55,8 +56,9 @@ rand07 = { package = "rand", version = "0.7" }
|
|||||||
prost-build = "0.9"
|
prost-build = "0.9"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["secp256k1"]
|
default = [ "secp256k1", "ecdsa" ]
|
||||||
secp256k1 = ["libsecp256k1"]
|
secp256k1 = [ "libsecp256k1" ]
|
||||||
|
ecdsa = [ "p256" ]
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "peer_id"
|
name = "peer_id"
|
||||||
|
@ -32,6 +32,8 @@
|
|||||||
//! (e.g. [ed25519 binary format](https://datatracker.ietf.org/doc/html/rfc8032#section-5.1.5)).
|
//! (e.g. [ed25519 binary format](https://datatracker.ietf.org/doc/html/rfc8032#section-5.1.5)).
|
||||||
//! All key types have functions to enable conversion to/from their binary representations.
|
//! All key types have functions to enable conversion to/from their binary representations.
|
||||||
|
|
||||||
|
#[cfg(feature = "ecdsa")]
|
||||||
|
pub mod ecdsa;
|
||||||
pub mod ed25519;
|
pub mod ed25519;
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
pub mod rsa;
|
pub mod rsa;
|
||||||
@ -71,6 +73,9 @@ pub enum Keypair {
|
|||||||
/// A Secp256k1 keypair.
|
/// A Secp256k1 keypair.
|
||||||
#[cfg(feature = "secp256k1")]
|
#[cfg(feature = "secp256k1")]
|
||||||
Secp256k1(secp256k1::Keypair),
|
Secp256k1(secp256k1::Keypair),
|
||||||
|
/// An ECDSA keypair.
|
||||||
|
#[cfg(feature = "ecdsa")]
|
||||||
|
Ecdsa(ecdsa::Keypair),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Keypair {
|
impl Keypair {
|
||||||
@ -85,6 +90,12 @@ impl Keypair {
|
|||||||
Keypair::Secp256k1(secp256k1::Keypair::generate())
|
Keypair::Secp256k1(secp256k1::Keypair::generate())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generate a new ECDSA keypair.
|
||||||
|
#[cfg(feature = "ecdsa")]
|
||||||
|
pub fn generate_ecdsa() -> Keypair {
|
||||||
|
Keypair::Ecdsa(ecdsa::Keypair::generate())
|
||||||
|
}
|
||||||
|
|
||||||
/// Decode an keypair from a DER-encoded secret key in PKCS#8 PrivateKeyInfo
|
/// Decode an keypair from a DER-encoded secret key in PKCS#8 PrivateKeyInfo
|
||||||
/// format (i.e. unencrypted) as defined in [RFC5208].
|
/// format (i.e. unencrypted) as defined in [RFC5208].
|
||||||
///
|
///
|
||||||
@ -114,6 +125,8 @@ impl Keypair {
|
|||||||
Rsa(ref pair) => pair.sign(msg),
|
Rsa(ref pair) => pair.sign(msg),
|
||||||
#[cfg(feature = "secp256k1")]
|
#[cfg(feature = "secp256k1")]
|
||||||
Secp256k1(ref pair) => pair.secret().sign(msg),
|
Secp256k1(ref pair) => pair.secret().sign(msg),
|
||||||
|
#[cfg(feature = "ecdsa")]
|
||||||
|
Ecdsa(ref pair) => Ok(pair.secret().sign(msg)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,6 +139,8 @@ impl Keypair {
|
|||||||
Rsa(pair) => PublicKey::Rsa(pair.public()),
|
Rsa(pair) => PublicKey::Rsa(pair.public()),
|
||||||
#[cfg(feature = "secp256k1")]
|
#[cfg(feature = "secp256k1")]
|
||||||
Secp256k1(pair) => PublicKey::Secp256k1(pair.public().clone()),
|
Secp256k1(pair) => PublicKey::Secp256k1(pair.public().clone()),
|
||||||
|
#[cfg(feature = "ecdsa")]
|
||||||
|
Ecdsa(pair) => PublicKey::Ecdsa(pair.public().clone()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,6 +165,12 @@ impl Keypair {
|
|||||||
"Encoding Secp256k1 key into Protobuf is unsupported",
|
"Encoding Secp256k1 key into Protobuf is unsupported",
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
#[cfg(feature = "ecdsa")]
|
||||||
|
Self::Ecdsa(_) => {
|
||||||
|
return Err(DecodingError::new(
|
||||||
|
"Encoding ECDSA key into Protobuf is unsupported",
|
||||||
|
))
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(pk.encode_to_vec())
|
Ok(pk.encode_to_vec())
|
||||||
@ -177,6 +198,9 @@ impl Keypair {
|
|||||||
keys_proto::KeyType::Secp256k1 => Err(DecodingError::new(
|
keys_proto::KeyType::Secp256k1 => Err(DecodingError::new(
|
||||||
"Decoding Secp256k1 key from Protobuf is unsupported.",
|
"Decoding Secp256k1 key from Protobuf is unsupported.",
|
||||||
)),
|
)),
|
||||||
|
keys_proto::KeyType::Ecdsa => Err(DecodingError::new(
|
||||||
|
"Decoding ECDSA key from Protobuf is unsupported.",
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -199,6 +223,9 @@ pub enum PublicKey {
|
|||||||
#[cfg(feature = "secp256k1")]
|
#[cfg(feature = "secp256k1")]
|
||||||
/// A public Secp256k1 key.
|
/// A public Secp256k1 key.
|
||||||
Secp256k1(secp256k1::PublicKey),
|
Secp256k1(secp256k1::PublicKey),
|
||||||
|
/// A public ECDSA key.
|
||||||
|
#[cfg(feature = "ecdsa")]
|
||||||
|
Ecdsa(ecdsa::PublicKey),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PublicKey {
|
impl PublicKey {
|
||||||
@ -215,6 +242,8 @@ impl PublicKey {
|
|||||||
Rsa(pk) => pk.verify(msg, sig),
|
Rsa(pk) => pk.verify(msg, sig),
|
||||||
#[cfg(feature = "secp256k1")]
|
#[cfg(feature = "secp256k1")]
|
||||||
Secp256k1(pk) => pk.verify(msg, sig),
|
Secp256k1(pk) => pk.verify(msg, sig),
|
||||||
|
#[cfg(feature = "ecdsa")]
|
||||||
|
Ecdsa(pk) => pk.verify(msg, sig),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -266,6 +295,11 @@ impl From<&PublicKey> for keys_proto::PublicKey {
|
|||||||
r#type: keys_proto::KeyType::Secp256k1 as i32,
|
r#type: keys_proto::KeyType::Secp256k1 as i32,
|
||||||
data: key.encode().to_vec(),
|
data: key.encode().to_vec(),
|
||||||
},
|
},
|
||||||
|
#[cfg(feature = "ecdsa")]
|
||||||
|
PublicKey::Ecdsa(key) => keys_proto::PublicKey {
|
||||||
|
r#type: keys_proto::KeyType::Ecdsa as i32,
|
||||||
|
data: key.encode_der(),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -299,6 +333,15 @@ impl TryFrom<keys_proto::PublicKey> for PublicKey {
|
|||||||
log::debug!("support for secp256k1 was disabled at compile-time");
|
log::debug!("support for secp256k1 was disabled at compile-time");
|
||||||
Err(DecodingError::new("Unsupported"))
|
Err(DecodingError::new("Unsupported"))
|
||||||
}
|
}
|
||||||
|
#[cfg(feature = "ecdsa")]
|
||||||
|
keys_proto::KeyType::Ecdsa => {
|
||||||
|
ecdsa::PublicKey::decode_der(&pubkey.data).map(PublicKey::Ecdsa)
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "ecdsa"))]
|
||||||
|
keys_proto::KeyType::Ecdsa => {
|
||||||
|
log::debug!("support for ECDSA was disabled at compile-time");
|
||||||
|
Err(DecodingError::new("Unsupported"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
245
core/src/identity/ecdsa.rs
Normal file
245
core/src/identity/ecdsa.rs
Normal file
@ -0,0 +1,245 @@
|
|||||||
|
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
// copy of this software and associated documentation files (the "Software"),
|
||||||
|
// to deal in the Software without restriction, including without limitation
|
||||||
|
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
// and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
// Software is furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
//! ECDSA keys with secp256r1 curve support.
|
||||||
|
|
||||||
|
use super::error::DecodingError;
|
||||||
|
use core::fmt;
|
||||||
|
use p256::{
|
||||||
|
ecdsa::{
|
||||||
|
signature::{Signer, Verifier},
|
||||||
|
Signature, SigningKey, VerifyingKey,
|
||||||
|
},
|
||||||
|
EncodedPoint,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// An ECDSA keypair.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Keypair {
|
||||||
|
secret: SecretKey,
|
||||||
|
public: PublicKey,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Keypair {
|
||||||
|
/// Generate a new random ECDSA keypair.
|
||||||
|
pub fn generate() -> Keypair {
|
||||||
|
Keypair::from(SecretKey::generate())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sign a message using the private key of this keypair.
|
||||||
|
pub fn sign(&self, msg: &[u8]) -> Vec<u8> {
|
||||||
|
self.secret.sign(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the public key of this keypair.
|
||||||
|
pub fn public(&self) -> &PublicKey {
|
||||||
|
&self.public
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the secret key of this keypair.
|
||||||
|
pub fn secret(&self) -> &SecretKey {
|
||||||
|
&self.secret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Keypair {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.debug_struct("Keypair")
|
||||||
|
.field("public", &self.public())
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Promote an ECDSA secret key into a keypair.
|
||||||
|
impl From<SecretKey> for Keypair {
|
||||||
|
fn from(secret: SecretKey) -> Keypair {
|
||||||
|
let public = PublicKey(VerifyingKey::from(&secret.0));
|
||||||
|
Keypair { secret, public }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Demote an ECDSA keypair to a secret key.
|
||||||
|
impl From<Keypair> for SecretKey {
|
||||||
|
fn from(kp: Keypair) -> SecretKey {
|
||||||
|
kp.secret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An ECDSA secret key.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct SecretKey(SigningKey);
|
||||||
|
|
||||||
|
impl SecretKey {
|
||||||
|
/// Generate a new random ECDSA secret key.
|
||||||
|
pub fn generate() -> SecretKey {
|
||||||
|
SecretKey(SigningKey::random(rand::thread_rng()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sign a message with this secret key, producing a DER-encoded ECDSA signature.
|
||||||
|
pub fn sign(&self, msg: &[u8]) -> Vec<u8> {
|
||||||
|
self.0.sign(msg).to_der().as_bytes().to_owned()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Encode a secret key into a byte buffer.
|
||||||
|
pub fn to_bytes(&self) -> Vec<u8> {
|
||||||
|
self.0.to_bytes().to_vec()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Decode a secret key from a byte buffer.
|
||||||
|
pub fn from_bytes(buf: &[u8]) -> Result<Self, DecodingError> {
|
||||||
|
SigningKey::from_bytes(buf)
|
||||||
|
.map_err(|err| DecodingError::new("failed to parse ecdsa p256 secret key").source(err))
|
||||||
|
.map(SecretKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for SecretKey {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "SecretKey")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An ECDSA public key.
|
||||||
|
#[derive(Clone, PartialEq, Eq)]
|
||||||
|
pub struct PublicKey(VerifyingKey);
|
||||||
|
|
||||||
|
impl PublicKey {
|
||||||
|
/// Verify an ECDSA signature on a message using the public key.
|
||||||
|
pub fn verify(&self, msg: &[u8], sig: &[u8]) -> bool {
|
||||||
|
let sig = match Signature::from_der(sig) {
|
||||||
|
Ok(sig) => sig,
|
||||||
|
Err(_) => return false,
|
||||||
|
};
|
||||||
|
self.0.verify(msg, &sig).is_ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Decode a public key from a byte buffer without compression.
|
||||||
|
pub fn from_bytes(k: &[u8]) -> Result<PublicKey, DecodingError> {
|
||||||
|
let enc_pt = EncodedPoint::from_bytes(k).map_err(|_| {
|
||||||
|
DecodingError::new("failed to parse ecdsa p256 public key, bad point encoding")
|
||||||
|
})?;
|
||||||
|
|
||||||
|
VerifyingKey::from_encoded_point(&enc_pt)
|
||||||
|
.map_err(|err| DecodingError::new("failed to parse ecdsa p256 public key").source(err))
|
||||||
|
.map(PublicKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Encode a public key into a byte buffer without compression.
|
||||||
|
pub fn to_bytes(&self) -> Vec<u8> {
|
||||||
|
self.0.to_encoded_point(false).as_bytes().to_owned()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Encode a public key into a DER encoded byte buffer as defined by SEC1 standard.
|
||||||
|
pub fn encode_der(&self) -> Vec<u8> {
|
||||||
|
let buf = self.to_bytes();
|
||||||
|
Self::add_asn1_header(&buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Decode a public key into a DER encoded byte buffer as defined by SEC1 standard.
|
||||||
|
pub fn decode_der(k: &[u8]) -> Result<PublicKey, DecodingError> {
|
||||||
|
let buf = Self::del_asn1_header(k).ok_or_else(|| {
|
||||||
|
DecodingError::new("failed to parse asn.1 encoded ecdsa p256 public key")
|
||||||
|
})?;
|
||||||
|
Self::from_bytes(&buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ecPublicKey (ANSI X9.62 public key type) OID: 1.2.840.10045.2.1
|
||||||
|
const EC_PUBLIC_KEY_OID: [u8; 9] = [0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01];
|
||||||
|
// secp256r1 OID: 1.2.840.10045.3.1.7
|
||||||
|
const SECP_256_R1_OID: [u8; 10] = [0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07];
|
||||||
|
|
||||||
|
// Add ASN1 header.
|
||||||
|
fn add_asn1_header(key_buf: &[u8]) -> Vec<u8> {
|
||||||
|
// ASN.1 struct type and length.
|
||||||
|
let mut asn1_buf = vec![
|
||||||
|
0x30,
|
||||||
|
0x00,
|
||||||
|
0x30,
|
||||||
|
(Self::EC_PUBLIC_KEY_OID.len() + Self::SECP_256_R1_OID.len()) as u8,
|
||||||
|
];
|
||||||
|
// Append OIDs.
|
||||||
|
asn1_buf.extend_from_slice(&Self::EC_PUBLIC_KEY_OID);
|
||||||
|
asn1_buf.extend_from_slice(&Self::SECP_256_R1_OID);
|
||||||
|
// Append key bitstring type and length.
|
||||||
|
asn1_buf.extend_from_slice(&[0x03, (key_buf.len() + 1) as u8, 0x00]);
|
||||||
|
// Append key bitstring value.
|
||||||
|
asn1_buf.extend_from_slice(key_buf);
|
||||||
|
// Update overall length field.
|
||||||
|
asn1_buf[1] = (asn1_buf.len() - 2) as u8;
|
||||||
|
|
||||||
|
asn1_buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check and remove ASN.1 header.
|
||||||
|
fn del_asn1_header(asn1_buf: &[u8]) -> Option<&[u8]> {
|
||||||
|
let oids_len = Self::EC_PUBLIC_KEY_OID.len() + Self::SECP_256_R1_OID.len();
|
||||||
|
let asn1_head = asn1_buf.get(..4)?;
|
||||||
|
let oids_buf = asn1_buf.get(4..4 + oids_len)?;
|
||||||
|
let bitstr_head = asn1_buf.get(4 + oids_len..4 + oids_len + 3)?;
|
||||||
|
|
||||||
|
// Sanity check
|
||||||
|
if asn1_head[0] != 0x30
|
||||||
|
|| asn1_head[2] != 0x30
|
||||||
|
|| asn1_head[3] as usize != oids_len
|
||||||
|
|| &oids_buf[..Self::EC_PUBLIC_KEY_OID.len()] != &Self::EC_PUBLIC_KEY_OID
|
||||||
|
|| &oids_buf[Self::EC_PUBLIC_KEY_OID.len()..] != &Self::SECP_256_R1_OID
|
||||||
|
|| bitstr_head[0] != 0x03
|
||||||
|
|| bitstr_head[2] != 0x00
|
||||||
|
{
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let key_len = bitstr_head[1].checked_sub(1)? as usize;
|
||||||
|
let key_buf = asn1_buf.get(4 + oids_len + 3..4 + oids_len + 3 + key_len as usize)?;
|
||||||
|
Some(key_buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for PublicKey {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.write_str("PublicKey(asn.1 uncompressed): ")?;
|
||||||
|
for byte in &self.encode_der() {
|
||||||
|
write!(f, "{:x}", byte)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sign_verify() {
|
||||||
|
let pair = Keypair::generate();
|
||||||
|
let pk = pair.public();
|
||||||
|
|
||||||
|
let msg = "hello world".as_bytes();
|
||||||
|
let sig = pair.sign(msg);
|
||||||
|
assert!(pk.verify(msg, &sig));
|
||||||
|
|
||||||
|
let mut invalid_sig = sig.clone();
|
||||||
|
invalid_sig[3..6].copy_from_slice(&[10, 23, 42]);
|
||||||
|
assert!(!pk.verify(msg, &invalid_sig));
|
||||||
|
|
||||||
|
let invalid_msg = "h3ll0 w0rld".as_bytes();
|
||||||
|
assert!(!pk.verify(invalid_msg, &sig));
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,7 @@ enum KeyType {
|
|||||||
RSA = 0;
|
RSA = 0;
|
||||||
Ed25519 = 1;
|
Ed25519 = 1;
|
||||||
Secp256k1 = 2;
|
Secp256k1 = 2;
|
||||||
|
ECDSA = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message PublicKey {
|
message PublicKey {
|
||||||
|
Reference in New Issue
Block a user