use crate::crypto::traits::{PublicKey, Signature, Signer, Verifier};
use crate::error::{AptosError, AptosResult};
use k256::ecdsa::{
Signature as K256Signature, SigningKey, VerifyingKey, signature::Signer as K256Signer,
signature::Verifier as K256Verifier,
};
use serde::{Deserialize, Serialize};
use std::fmt;
use zeroize::Zeroize;
pub const SECP256K1_PRIVATE_KEY_LENGTH: usize = 32;
pub const SECP256K1_PUBLIC_KEY_LENGTH: usize = 33;
#[allow(dead_code)] pub const SECP256K1_PUBLIC_KEY_UNCOMPRESSED_LENGTH: usize = 65;
pub const SECP256K1_SIGNATURE_LENGTH: usize = 64;
#[derive(Clone, Zeroize)]
#[zeroize(drop)]
pub struct Secp256k1PrivateKey {
#[zeroize(skip)]
#[allow(unused)] inner: SigningKey,
}
impl Secp256k1PrivateKey {
pub fn generate() -> Self {
let signing_key = SigningKey::random(&mut rand::rngs::OsRng);
Self { inner: signing_key }
}
pub fn from_bytes(bytes: &[u8]) -> AptosResult<Self> {
if bytes.len() != SECP256K1_PRIVATE_KEY_LENGTH {
return Err(AptosError::InvalidPrivateKey(format!(
"expected {} bytes, got {}",
SECP256K1_PRIVATE_KEY_LENGTH,
bytes.len()
)));
}
let signing_key = SigningKey::from_slice(bytes)
.map_err(|e| AptosError::InvalidPrivateKey(e.to_string()))?;
Ok(Self { inner: signing_key })
}
pub fn from_hex(hex_str: &str) -> AptosResult<Self> {
let bytes = const_hex::decode(hex_str)?;
Self::from_bytes(&bytes)
}
pub fn from_aip80(s: &str) -> AptosResult<Self> {
const PREFIX: &str = "secp256k1-priv-";
if let Some(hex_part) = s.strip_prefix(PREFIX) {
Self::from_hex(hex_part)
} else {
Err(AptosError::InvalidPrivateKey(format!(
"invalid AIP-80 format: expected prefix '{PREFIX}'"
)))
}
}
pub fn to_bytes(&self) -> [u8; SECP256K1_PRIVATE_KEY_LENGTH] {
self.inner.to_bytes().into()
}
pub fn to_hex(&self) -> String {
const_hex::encode_prefixed(self.inner.to_bytes())
}
pub fn to_aip80(&self) -> String {
format!("secp256k1-priv-{}", self.to_hex())
}
pub fn public_key(&self) -> Secp256k1PublicKey {
Secp256k1PublicKey {
inner: *self.inner.verifying_key(),
}
}
pub fn sign(&self, message: &[u8]) -> Secp256k1Signature {
let hash = crate::crypto::sha2_256(message);
let signature: K256Signature = self.inner.sign(&hash);
let normalized = signature.normalize_s().unwrap_or(signature);
Secp256k1Signature { inner: normalized }
}
pub fn sign_prehashed(&self, hash: &[u8; 32]) -> Secp256k1Signature {
let signature: K256Signature = self.inner.sign(hash);
let normalized = signature.normalize_s().unwrap_or(signature);
Secp256k1Signature { inner: normalized }
}
}
impl Signer for Secp256k1PrivateKey {
type Signature = Secp256k1Signature;
fn sign(&self, message: &[u8]) -> Secp256k1Signature {
Secp256k1PrivateKey::sign(self, message)
}
fn public_key(&self) -> Secp256k1PublicKey {
Secp256k1PrivateKey::public_key(self)
}
}
impl fmt::Debug for Secp256k1PrivateKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Secp256k1PrivateKey([REDACTED])")
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct Secp256k1PublicKey {
inner: VerifyingKey,
}
impl Secp256k1PublicKey {
pub fn from_bytes(bytes: &[u8]) -> AptosResult<Self> {
let verifying_key = VerifyingKey::from_sec1_bytes(bytes)
.map_err(|e| AptosError::InvalidPublicKey(e.to_string()))?;
Ok(Self {
inner: verifying_key,
})
}
pub fn from_hex(hex_str: &str) -> AptosResult<Self> {
let bytes = const_hex::decode(hex_str)?;
Self::from_bytes(&bytes)
}
pub fn from_aip80(s: &str) -> AptosResult<Self> {
const PREFIX: &str = "secp256k1-pub-";
if let Some(hex_part) = s.strip_prefix(PREFIX) {
Self::from_hex(hex_part)
} else {
Err(AptosError::InvalidPublicKey(format!(
"invalid AIP-80 format: expected prefix '{PREFIX}'"
)))
}
}
pub fn to_bytes(&self) -> Vec<u8> {
self.inner.to_sec1_bytes().to_vec()
}
pub fn to_uncompressed_bytes(&self) -> Vec<u8> {
#[allow(unused_imports)]
use k256::elliptic_curve::sec1::ToEncodedPoint;
self.inner.to_encoded_point(false).as_bytes().to_vec()
}
pub fn to_hex(&self) -> String {
const_hex::encode_prefixed(self.to_bytes())
}
pub fn to_aip80(&self) -> String {
format!("secp256k1-pub-{}", self.to_hex())
}
pub fn verify(&self, message: &[u8], signature: &Secp256k1Signature) -> AptosResult<()> {
if signature.inner.normalize_s().is_some() {
return Err(AptosError::SignatureVerificationFailed);
}
let hash = crate::crypto::sha2_256(message);
self.inner
.verify(&hash, &signature.inner)
.map_err(|_| AptosError::SignatureVerificationFailed)
}
pub fn verify_prehashed(
&self,
hash: &[u8; 32],
signature: &Secp256k1Signature,
) -> AptosResult<()> {
if signature.inner.normalize_s().is_some() {
return Err(AptosError::SignatureVerificationFailed);
}
self.inner
.verify(hash, &signature.inner)
.map_err(|_| AptosError::SignatureVerificationFailed)
}
pub fn to_address(&self) -> crate::types::AccountAddress {
let uncompressed = self.to_uncompressed_bytes();
let mut bcs_bytes = Vec::with_capacity(1 + 1 + uncompressed.len());
bcs_bytes.push(0x01); bcs_bytes.push(65); bcs_bytes.extend_from_slice(&uncompressed);
crate::crypto::derive_address(&bcs_bytes, crate::crypto::SINGLE_KEY_SCHEME)
}
}
impl PublicKey for Secp256k1PublicKey {
const LENGTH: usize = SECP256K1_PUBLIC_KEY_LENGTH;
fn from_bytes(bytes: &[u8]) -> AptosResult<Self> {
Secp256k1PublicKey::from_bytes(bytes)
}
fn to_bytes(&self) -> Vec<u8> {
Secp256k1PublicKey::to_bytes(self)
}
}
impl Verifier for Secp256k1PublicKey {
type Signature = Secp256k1Signature;
fn verify(&self, message: &[u8], signature: &Secp256k1Signature) -> AptosResult<()> {
Secp256k1PublicKey::verify(self, message, signature)
}
}
impl fmt::Debug for Secp256k1PublicKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Secp256k1PublicKey({})", self.to_hex())
}
}
impl fmt::Display for Secp256k1PublicKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.to_hex())
}
}
impl Serialize for Secp256k1PublicKey {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
if serializer.is_human_readable() {
serializer.serialize_str(&self.to_hex())
} else {
serializer.serialize_bytes(&self.to_bytes())
}
}
}
impl<'de> Deserialize<'de> for Secp256k1PublicKey {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
if deserializer.is_human_readable() {
let s = String::deserialize(deserializer)?;
Self::from_hex(&s).map_err(serde::de::Error::custom)
} else {
let bytes = Vec::<u8>::deserialize(deserializer)?;
Self::from_bytes(&bytes).map_err(serde::de::Error::custom)
}
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct Secp256k1Signature {
inner: K256Signature,
}
impl Secp256k1Signature {
pub fn from_bytes(bytes: &[u8]) -> AptosResult<Self> {
if bytes.len() != SECP256K1_SIGNATURE_LENGTH {
return Err(AptosError::InvalidSignature(format!(
"expected {} bytes, got {}",
SECP256K1_SIGNATURE_LENGTH,
bytes.len()
)));
}
let signature = K256Signature::from_slice(bytes)
.map_err(|e| AptosError::InvalidSignature(e.to_string()))?;
if signature.normalize_s().is_some() {
return Err(AptosError::InvalidSignature(
"high-S signature rejected: Aptos requires low-S (canonical) ECDSA signatures"
.into(),
));
}
Ok(Self { inner: signature })
}
pub fn from_hex(hex_str: &str) -> AptosResult<Self> {
let bytes = const_hex::decode(hex_str)?;
Self::from_bytes(&bytes)
}
pub fn to_bytes(&self) -> [u8; SECP256K1_SIGNATURE_LENGTH] {
self.inner.to_bytes().into()
}
pub fn to_hex(&self) -> String {
const_hex::encode_prefixed(self.to_bytes())
}
}
impl Signature for Secp256k1Signature {
type PublicKey = Secp256k1PublicKey;
const LENGTH: usize = SECP256K1_SIGNATURE_LENGTH;
fn from_bytes(bytes: &[u8]) -> AptosResult<Self> {
Secp256k1Signature::from_bytes(bytes)
}
fn to_bytes(&self) -> Vec<u8> {
self.inner.to_bytes().to_vec()
}
}
impl fmt::Debug for Secp256k1Signature {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Secp256k1Signature({})", self.to_hex())
}
}
impl fmt::Display for Secp256k1Signature {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.to_hex())
}
}
impl Serialize for Secp256k1Signature {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
if serializer.is_human_readable() {
serializer.serialize_str(&self.to_hex())
} else {
serializer.serialize_bytes(&self.to_bytes())
}
}
}
impl<'de> Deserialize<'de> for Secp256k1Signature {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
if deserializer.is_human_readable() {
let s = String::deserialize(deserializer)?;
Self::from_hex(&s).map_err(serde::de::Error::custom)
} else {
let bytes = Vec::<u8>::deserialize(deserializer)?;
Self::from_bytes(&bytes).map_err(serde::de::Error::custom)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_generate_and_sign() {
let private_key = Secp256k1PrivateKey::generate();
let message = b"hello world";
let signature = private_key.sign(message);
let public_key = private_key.public_key();
assert!(public_key.verify(message, &signature).is_ok());
}
#[test]
fn test_wrong_message_fails() {
let private_key = Secp256k1PrivateKey::generate();
let message = b"hello world";
let wrong_message = b"hello world!";
let signature = private_key.sign(message);
let public_key = private_key.public_key();
assert!(public_key.verify(wrong_message, &signature).is_err());
}
#[test]
fn test_from_bytes_roundtrip() {
let private_key = Secp256k1PrivateKey::generate();
let bytes = private_key.to_bytes();
let restored = Secp256k1PrivateKey::from_bytes(&bytes).unwrap();
assert_eq!(private_key.to_bytes(), restored.to_bytes());
}
#[test]
fn test_public_key_compressed() {
let private_key = Secp256k1PrivateKey::generate();
let public_key = private_key.public_key();
assert_eq!(public_key.to_bytes().len(), 33);
assert_eq!(public_key.to_uncompressed_bytes().len(), 65);
}
#[test]
fn test_public_key_from_bytes_roundtrip() {
let private_key = Secp256k1PrivateKey::generate();
let public_key = private_key.public_key();
let bytes = public_key.to_bytes();
let restored = Secp256k1PublicKey::from_bytes(&bytes).unwrap();
assert_eq!(public_key.to_bytes(), restored.to_bytes());
}
#[test]
fn test_signature_from_bytes_roundtrip() {
let private_key = Secp256k1PrivateKey::generate();
let signature = private_key.sign(b"test");
let bytes = signature.to_bytes();
let restored = Secp256k1Signature::from_bytes(&bytes).unwrap();
assert_eq!(signature.to_bytes(), restored.to_bytes());
}
#[test]
fn test_hex_roundtrip() {
let private_key = Secp256k1PrivateKey::generate();
let hex = private_key.to_hex();
let restored = Secp256k1PrivateKey::from_hex(&hex).unwrap();
assert_eq!(private_key.to_bytes(), restored.to_bytes());
}
#[test]
fn test_public_key_hex_roundtrip() {
let private_key = Secp256k1PrivateKey::generate();
let public_key = private_key.public_key();
let hex = public_key.to_hex();
let restored = Secp256k1PublicKey::from_hex(&hex).unwrap();
assert_eq!(public_key.to_bytes(), restored.to_bytes());
}
#[test]
fn test_signature_hex_roundtrip() {
let private_key = Secp256k1PrivateKey::generate();
let signature = private_key.sign(b"test");
let hex = signature.to_hex();
let restored = Secp256k1Signature::from_hex(&hex).unwrap();
assert_eq!(signature.to_bytes(), restored.to_bytes());
}
#[test]
fn test_invalid_private_key_bytes() {
let bytes = vec![0u8; 16]; let result = Secp256k1PrivateKey::from_bytes(&bytes);
assert!(result.is_err());
}
#[test]
fn test_invalid_public_key_bytes() {
let bytes = vec![0u8; 16]; let result = Secp256k1PublicKey::from_bytes(&bytes);
assert!(result.is_err());
}
#[test]
fn test_invalid_signature_bytes() {
let bytes = vec![0u8; 16]; let result = Secp256k1Signature::from_bytes(&bytes);
assert!(result.is_err());
}
#[test]
fn test_high_s_signature_rejected() {
use k256::elliptic_curve::ops::Neg;
let private_key = Secp256k1PrivateKey::generate();
let signature = private_key.sign(b"test message");
let low_s_sig = k256::ecdsa::Signature::from_slice(&signature.to_bytes()).unwrap();
let (r, s) = low_s_sig.split_scalars();
let neg_s = s.neg();
let high_s_sig = k256::ecdsa::Signature::from_scalars(r, neg_s).unwrap();
assert!(
high_s_sig.normalize_s().is_some(),
"constructed signature should be high-S"
);
let high_s_bytes = high_s_sig.to_bytes();
let result = Secp256k1Signature::from_bytes(&high_s_bytes);
assert!(result.is_err(), "high-S signature should be rejected");
assert!(
result
.unwrap_err()
.to_string()
.contains("high-S signature rejected"),
"error message should mention high-S rejection"
);
let sig_with_high_s = Secp256k1Signature { inner: high_s_sig };
let public_key = private_key.public_key();
let result = public_key.verify(b"test message", &sig_with_high_s);
assert!(result.is_err(), "verify should reject high-S signature");
}
#[test]
fn test_signing_always_produces_low_s() {
for _ in 0..20 {
let private_key = Secp256k1PrivateKey::generate();
let signature = private_key.sign(b"test low-s");
assert!(
signature.inner.normalize_s().is_none(),
"signing must always produce low-S signatures"
);
}
}
#[test]
fn test_json_serialization_public_key() {
let private_key = Secp256k1PrivateKey::generate();
let public_key = private_key.public_key();
let json = serde_json::to_string(&public_key).unwrap();
let restored: Secp256k1PublicKey = serde_json::from_str(&json).unwrap();
assert_eq!(public_key.to_bytes(), restored.to_bytes());
}
#[test]
fn test_json_serialization_signature() {
let private_key = Secp256k1PrivateKey::generate();
let signature = private_key.sign(b"test");
let json = serde_json::to_string(&signature).unwrap();
let restored: Secp256k1Signature = serde_json::from_str(&json).unwrap();
assert_eq!(signature.to_bytes(), restored.to_bytes());
}
#[test]
fn test_key_lengths() {
assert_eq!(Secp256k1PublicKey::LENGTH, SECP256K1_PUBLIC_KEY_LENGTH);
assert_eq!(Secp256k1Signature::LENGTH, SECP256K1_SIGNATURE_LENGTH);
}
#[test]
fn test_display_debug() {
let private_key = Secp256k1PrivateKey::generate();
let public_key = private_key.public_key();
let signature = private_key.sign(b"test");
assert!(format!("{public_key:?}").contains("Secp256k1PublicKey"));
assert!(format!("{signature:?}").contains("Secp256k1Signature"));
assert!(format!("{public_key}").starts_with("0x"));
assert!(format!("{signature}").starts_with("0x"));
}
#[test]
fn test_private_key_aip80_roundtrip() {
let private_key = Secp256k1PrivateKey::generate();
let aip80 = private_key.to_aip80();
assert!(aip80.starts_with("secp256k1-priv-0x"));
let restored = Secp256k1PrivateKey::from_aip80(&aip80).unwrap();
assert_eq!(private_key.to_bytes(), restored.to_bytes());
}
#[test]
fn test_private_key_aip80_format() {
let bytes = [0x01; 32];
let private_key = Secp256k1PrivateKey::from_bytes(&bytes).unwrap();
let aip80 = private_key.to_aip80();
let expected = format!("secp256k1-priv-0x{}", "01".repeat(32));
assert_eq!(aip80, expected);
}
#[test]
fn test_private_key_aip80_invalid_prefix() {
let result = Secp256k1PrivateKey::from_aip80("ed25519-priv-0x01");
assert!(result.is_err());
}
#[test]
fn test_public_key_aip80_roundtrip() {
let private_key = Secp256k1PrivateKey::generate();
let public_key = private_key.public_key();
let aip80 = public_key.to_aip80();
assert!(aip80.starts_with("secp256k1-pub-0x"));
let restored = Secp256k1PublicKey::from_aip80(&aip80).unwrap();
assert_eq!(public_key.to_bytes(), restored.to_bytes());
}
#[test]
fn test_public_key_aip80_invalid_prefix() {
let result = Secp256k1PublicKey::from_aip80("ed25519-pub-0x01");
assert!(result.is_err());
}
#[test]
fn test_signer_trait() {
use crate::crypto::traits::Signer;
let private_key = Secp256k1PrivateKey::generate();
let message = b"trait test";
let signature = Signer::sign(&private_key, message);
let public_key = Signer::public_key(&private_key);
assert!(public_key.verify(message, &signature).is_ok());
}
#[test]
fn test_verifier_trait() {
use crate::crypto::traits::Verifier;
let private_key = Secp256k1PrivateKey::generate();
let public_key = private_key.public_key();
let message = b"verifier test";
let signature = private_key.sign(message);
assert!(Verifier::verify(&public_key, message, &signature).is_ok());
}
#[test]
fn test_public_key_trait() {
use crate::crypto::traits::PublicKey;
let private_key = Secp256k1PrivateKey::generate();
let public_key = private_key.public_key();
let bytes = PublicKey::to_bytes(&public_key);
let restored = Secp256k1PublicKey::from_bytes(&bytes).unwrap();
assert_eq!(public_key.to_bytes(), restored.to_bytes());
}
#[test]
fn test_signature_trait() {
use crate::crypto::traits::Signature;
let private_key = Secp256k1PrivateKey::generate();
let signature = private_key.sign(b"test");
let bytes = Signature::to_bytes(&signature);
let restored = Secp256k1Signature::from_bytes(&bytes).unwrap();
assert_eq!(signature.to_bytes(), restored.to_bytes());
}
#[test]
fn test_private_key_debug() {
let private_key = Secp256k1PrivateKey::generate();
let debug = format!("{private_key:?}");
assert!(debug.contains("REDACTED"));
assert!(!debug.contains(&private_key.to_hex()));
}
#[test]
fn test_address_derivation() {
let private_key = Secp256k1PrivateKey::generate();
let public_key = private_key.public_key();
let address = public_key.to_address();
assert!(!address.is_zero());
let address2 = public_key.to_address();
assert_eq!(address, address2);
}
#[test]
fn test_uncompressed_bytes() {
let private_key = Secp256k1PrivateKey::generate();
let public_key = private_key.public_key();
let uncompressed = public_key.to_uncompressed_bytes();
assert_eq!(uncompressed.len(), 65);
assert_eq!(uncompressed[0], 0x04); }
#[test]
fn test_private_key_clone() {
let private_key = Secp256k1PrivateKey::generate();
let cloned = private_key.clone();
assert_eq!(private_key.to_bytes(), cloned.to_bytes());
}
#[test]
fn test_public_key_equality() {
let private_key = Secp256k1PrivateKey::generate();
let pk1 = private_key.public_key();
let pk2 = private_key.public_key();
assert_eq!(pk1, pk2);
let different = Secp256k1PrivateKey::generate().public_key();
assert_ne!(pk1, different);
}
#[test]
fn test_signature_equality() {
let private_key = Secp256k1PrivateKey::generate();
let sig1 = private_key.sign(b"test");
let sig2 = private_key.sign(b"test");
let public_key = private_key.public_key();
assert!(public_key.verify(b"test", &sig1).is_ok());
assert!(public_key.verify(b"test", &sig2).is_ok());
}
}