ssh: remove unused traits, simplify code
Signed-off-by: Alexis Sellier <alexis@radicle.xyz>
This commit is contained in:
parent
c294f2ab5c
commit
0482f0ce04
|
|
@ -11,9 +11,8 @@ use zeroize::Zeroize as _;
|
||||||
|
|
||||||
use crate::agent::msg;
|
use crate::agent::msg;
|
||||||
use crate::agent::Constraint;
|
use crate::agent::Constraint;
|
||||||
use crate::encoding;
|
use crate::encoding::{self, Encodable};
|
||||||
use crate::encoding::{Buffer, Encoding, Reader};
|
use crate::encoding::{Buffer, Encoding, Reader};
|
||||||
use crate::key::{Private, Public};
|
|
||||||
|
|
||||||
/// An ed25519 Signature.
|
/// An ed25519 Signature.
|
||||||
pub type Signature = [u8; 64];
|
pub type Signature = [u8; 64];
|
||||||
|
|
@ -87,7 +86,7 @@ impl<S: ClientStream> AgentClient<S> {
|
||||||
/// to apply when using the key to sign.
|
/// to apply when using the key to sign.
|
||||||
pub fn add_identity<K>(&mut self, key: &K, constraints: &[Constraint]) -> Result<(), Error>
|
pub fn add_identity<K>(&mut self, key: &K, constraints: &[Constraint]) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
K: Private,
|
K: Encodable,
|
||||||
K::Error: std::error::Error + Send + Sync + 'static,
|
K::Error: std::error::Error + Send + Sync + 'static,
|
||||||
{
|
{
|
||||||
self.buf.zeroize();
|
self.buf.zeroize();
|
||||||
|
|
@ -98,8 +97,7 @@ impl<S: ClientStream> AgentClient<S> {
|
||||||
} else {
|
} else {
|
||||||
self.buf.push(msg::ADD_ID_CONSTRAINED)
|
self.buf.push(msg::ADD_ID_CONSTRAINED)
|
||||||
}
|
}
|
||||||
key.write(&mut self.buf)
|
key.write(&mut self.buf);
|
||||||
.map_err(|err| Error::Private(Box::new(err)))?;
|
|
||||||
|
|
||||||
if !constraints.is_empty() {
|
if !constraints.is_empty() {
|
||||||
for cons in constraints {
|
for cons in constraints {
|
||||||
|
|
@ -203,7 +201,7 @@ impl<S: ClientStream> AgentClient<S> {
|
||||||
/// keys.
|
/// keys.
|
||||||
pub fn request_identities<K>(&mut self) -> Result<Vec<K>, Error>
|
pub fn request_identities<K>(&mut self) -> Result<Vec<K>, Error>
|
||||||
where
|
where
|
||||||
K: Public,
|
K: Encodable,
|
||||||
K::Error: std::error::Error + Send + Sync + 'static,
|
K::Error: std::error::Error + Send + Sync + 'static,
|
||||||
{
|
{
|
||||||
self.buf.zeroize();
|
self.buf.zeroize();
|
||||||
|
|
@ -224,7 +222,7 @@ impl<S: ClientStream> AgentClient<S> {
|
||||||
let _ = r.read_string()?;
|
let _ = r.read_string()?;
|
||||||
let mut r = key.reader(0);
|
let mut r = key.reader(0);
|
||||||
|
|
||||||
if let Some(pk) = K::read(&mut r).map_err(|err| Error::Public(Box::new(err)))? {
|
if let Ok(pk) = K::read(&mut r) {
|
||||||
keys.push(pk);
|
keys.push(pk);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -236,7 +234,7 @@ impl<S: ClientStream> AgentClient<S> {
|
||||||
/// Ask the agent to sign the supplied piece of data.
|
/// Ask the agent to sign the supplied piece of data.
|
||||||
pub fn sign_request<K>(&mut self, public: &K, data: Buffer) -> Result<Signature, Error>
|
pub fn sign_request<K>(&mut self, public: &K, data: Buffer) -> Result<Signature, Error>
|
||||||
where
|
where
|
||||||
K: Public + fmt::Debug,
|
K: Encodable + fmt::Debug,
|
||||||
{
|
{
|
||||||
self.prepare_sign_request(public, &data);
|
self.prepare_sign_request(public, &data);
|
||||||
self.stream.read_response(&mut self.buf)?;
|
self.stream.read_response(&mut self.buf)?;
|
||||||
|
|
@ -255,7 +253,7 @@ impl<S: ClientStream> AgentClient<S> {
|
||||||
|
|
||||||
fn prepare_sign_request<K>(&mut self, public: &K, data: &[u8])
|
fn prepare_sign_request<K>(&mut self, public: &K, data: &[u8])
|
||||||
where
|
where
|
||||||
K: Public + fmt::Debug,
|
K: Encodable + fmt::Debug,
|
||||||
{
|
{
|
||||||
// byte SSH_AGENTC_SIGN_REQUEST
|
// byte SSH_AGENTC_SIGN_REQUEST
|
||||||
// string key blob
|
// string key blob
|
||||||
|
|
@ -263,10 +261,9 @@ impl<S: ClientStream> AgentClient<S> {
|
||||||
// uint32 flags
|
// uint32 flags
|
||||||
|
|
||||||
let mut pk = Buffer::default();
|
let mut pk = Buffer::default();
|
||||||
let n = public.write(&mut pk);
|
public.write(&mut pk);
|
||||||
let total = 1 + n + 4 + data.len() + 4;
|
|
||||||
|
|
||||||
debug_assert_eq!(n, pk.len());
|
let total = 1 + pk.len() + 4 + data.len() + 4;
|
||||||
|
|
||||||
self.buf.zeroize();
|
self.buf.zeroize();
|
||||||
self.buf
|
self.buf
|
||||||
|
|
@ -294,13 +291,12 @@ impl<S: ClientStream> AgentClient<S> {
|
||||||
/// Ask the agent to remove a key from its memory.
|
/// Ask the agent to remove a key from its memory.
|
||||||
pub fn remove_identity<K>(&mut self, public: &K) -> Result<(), Error>
|
pub fn remove_identity<K>(&mut self, public: &K) -> Result<(), Error>
|
||||||
where
|
where
|
||||||
K: Public,
|
K: Encodable,
|
||||||
{
|
{
|
||||||
let mut pk: Buffer = Vec::new().into();
|
let mut pk: Buffer = Vec::new().into();
|
||||||
let n = public.write(&mut pk);
|
public.write(&mut pk);
|
||||||
let total = 1 + n;
|
|
||||||
|
|
||||||
debug_assert_eq!(n, pk.len());
|
let total = 1 + pk.len();
|
||||||
|
|
||||||
self.buf.zeroize();
|
self.buf.zeroize();
|
||||||
self.buf.write_u32::<BigEndian>(total as u32)?;
|
self.buf.write_u32::<BigEndian>(total as u32)?;
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
use std::mem::size_of;
|
|
||||||
use std::ops::DerefMut;
|
use std::ops::DerefMut;
|
||||||
|
|
||||||
use byteorder::{BigEndian, ByteOrder, WriteBytesExt};
|
use byteorder::{BigEndian, ByteOrder, WriteBytesExt};
|
||||||
|
|
@ -33,16 +32,15 @@ pub trait Encodable: Sized {
|
||||||
type Error: std::error::Error + Send + Sync + 'static;
|
type Error: std::error::Error + Send + Sync + 'static;
|
||||||
|
|
||||||
/// Read from the SSH format.
|
/// Read from the SSH format.
|
||||||
fn read_ssh(reader: &mut Cursor) -> Result<Self, Self::Error>;
|
fn read(reader: &mut Cursor) -> Result<Self, Self::Error>;
|
||||||
|
|
||||||
/// Write to the SSH format.
|
/// Write to the SSH format.
|
||||||
fn write_ssh<E: Encoding>(&self, buf: &mut E);
|
fn write<E: Encoding>(&self, buf: &mut E);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Encode in the SSH format.
|
/// Encode in the SSH format.
|
||||||
pub trait Encoding {
|
pub trait Encoding {
|
||||||
/// Push an SSH-encoded string to `self`.
|
/// Push an SSH-encoded string to `self`.
|
||||||
fn extend_ssh_string(&mut self, s: &[u8]) -> usize;
|
fn extend_ssh_string(&mut self, s: &[u8]);
|
||||||
/// Push an SSH-encoded blank string of length `s` to `self`.
|
/// Push an SSH-encoded blank string of length `s` to `self`.
|
||||||
fn extend_ssh_string_blank(&mut self, s: usize) -> &mut [u8];
|
fn extend_ssh_string_blank(&mut self, s: usize) -> &mut [u8];
|
||||||
/// Push an SSH-encoded multiple-precision integer.
|
/// Push an SSH-encoded multiple-precision integer.
|
||||||
|
|
@ -67,11 +65,9 @@ pub fn mpint_len(s: &[u8]) -> usize {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Encoding for Vec<u8> {
|
impl Encoding for Vec<u8> {
|
||||||
fn extend_ssh_string(&mut self, s: &[u8]) -> usize {
|
fn extend_ssh_string(&mut self, s: &[u8]) {
|
||||||
self.write_u32::<BigEndian>(s.len() as u32).unwrap();
|
self.write_u32::<BigEndian>(s.len() as u32).unwrap();
|
||||||
self.extend(s);
|
self.extend(s);
|
||||||
|
|
||||||
size_of::<u32>() + s.len()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extend_ssh_string_blank(&mut self, len: usize) -> &mut [u8] {
|
fn extend_ssh_string_blank(&mut self, len: usize) -> &mut [u8] {
|
||||||
|
|
@ -134,7 +130,7 @@ impl Encoding for Vec<u8> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Encoding for Buffer {
|
impl Encoding for Buffer {
|
||||||
fn extend_ssh_string(&mut self, s: &[u8]) -> usize {
|
fn extend_ssh_string(&mut self, s: &[u8]) {
|
||||||
self.deref_mut().extend_ssh_string(s)
|
self.deref_mut().extend_ssh_string(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
use std::error::Error;
|
|
||||||
|
|
||||||
use crate::encoding::{Buffer, Cursor, Encoding};
|
|
||||||
|
|
||||||
/// A public SSH key.
|
|
||||||
pub trait Public: Sized {
|
|
||||||
type Error: Error + Send + Sync + 'static;
|
|
||||||
|
|
||||||
/// Write the public key to the given buffer, in SSH "blob" format.
|
|
||||||
fn write<E: Encoding>(&self, buf: &mut E) -> usize;
|
|
||||||
/// Read the public key from the given reader.
|
|
||||||
fn read(reader: &mut Cursor) -> Result<Option<Self>, Self::Error>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A private SSH key.
|
|
||||||
pub trait Private: Sized {
|
|
||||||
type Error: Error + Send + Sync + 'static;
|
|
||||||
|
|
||||||
/// Read a private key from the given reader.
|
|
||||||
fn read(reader: &mut Cursor) -> Result<Option<Self>, Self::Error>;
|
|
||||||
/// Write the key bytes to the supplied buffer.
|
|
||||||
fn write(&self, buf: &mut Buffer) -> Result<(), Self::Error>;
|
|
||||||
/// Sign the data and write the signature to the given buffer.
|
|
||||||
fn write_signature<T: AsRef<[u8]>>(&self, data: T, buf: &mut Buffer)
|
|
||||||
-> Result<(), Self::Error>;
|
|
||||||
}
|
|
||||||
|
|
@ -1,3 +1,2 @@
|
||||||
pub mod agent;
|
pub mod agent;
|
||||||
pub mod encoding;
|
pub mod encoding;
|
||||||
pub mod key;
|
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,13 @@
|
||||||
pub mod agent;
|
pub mod agent;
|
||||||
|
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::ops::DerefMut;
|
|
||||||
|
|
||||||
use byteorder::{BigEndian, WriteBytesExt};
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use zeroize::Zeroizing;
|
|
||||||
|
|
||||||
use radicle_ssh::encoding;
|
use radicle_ssh::encoding;
|
||||||
use radicle_ssh::encoding::Encodable;
|
use radicle_ssh::encoding::Encodable;
|
||||||
use radicle_ssh::encoding::Encoding;
|
use radicle_ssh::encoding::Encoding;
|
||||||
use radicle_ssh::encoding::Reader;
|
use radicle_ssh::encoding::Reader;
|
||||||
use radicle_ssh::key::Public;
|
|
||||||
|
|
||||||
use crate::crypto;
|
use crate::crypto;
|
||||||
use crate::crypto::PublicKey;
|
use crate::crypto::PublicKey;
|
||||||
|
|
@ -90,7 +86,7 @@ pub enum SignatureError {
|
||||||
impl Encodable for crypto::Signature {
|
impl Encodable for crypto::Signature {
|
||||||
type Error = SignatureError;
|
type Error = SignatureError;
|
||||||
|
|
||||||
fn read_ssh(r: &mut encoding::Cursor) -> Result<Self, Self::Error> {
|
fn read(r: &mut encoding::Cursor) -> Result<Self, Self::Error> {
|
||||||
let buf = r.read_string()?;
|
let buf = r.read_string()?;
|
||||||
let mut inner_strs = buf.reader(0);
|
let mut inner_strs = buf.reader(0);
|
||||||
|
|
||||||
|
|
@ -105,7 +101,7 @@ impl Encodable for crypto::Signature {
|
||||||
Ok(sig)
|
Ok(sig)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_ssh<E: Encoding>(&self, buf: &mut E) {
|
fn write<E: Encoding>(&self, buf: &mut E) {
|
||||||
let mut inner_strs = Vec::new();
|
let mut inner_strs = Vec::new();
|
||||||
inner_strs.extend_ssh_string(b"ssh-ed25519");
|
inner_strs.extend_ssh_string(b"ssh-ed25519");
|
||||||
inner_strs.extend_ssh_string(self.as_ref());
|
inner_strs.extend_ssh_string(self.as_ref());
|
||||||
|
|
@ -126,12 +122,10 @@ pub enum PublicKeyError {
|
||||||
impl Encodable for PublicKey {
|
impl Encodable for PublicKey {
|
||||||
type Error = PublicKeyError;
|
type Error = PublicKeyError;
|
||||||
|
|
||||||
fn read_ssh(r: &mut encoding::Cursor) -> Result<Self, Self::Error> {
|
fn read(r: &mut encoding::Cursor) -> Result<Self, Self::Error> {
|
||||||
let buf = r.read_string()?;
|
match r.read_string()? {
|
||||||
let mut str_r = buf.reader(0);
|
|
||||||
match str_r.read_string()? {
|
|
||||||
b"ssh-ed25519" => {
|
b"ssh-ed25519" => {
|
||||||
let s = str_r.read_string()?;
|
let s = r.read_string()?;
|
||||||
let p = PublicKey::try_from(s)?;
|
let p = PublicKey::try_from(s)?;
|
||||||
|
|
||||||
Ok(p)
|
Ok(p)
|
||||||
|
|
@ -142,31 +136,11 @@ impl Encodable for PublicKey {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_ssh<E: Encoding>(&self, w: &mut E) {
|
fn write<E: Encoding>(&self, w: &mut E) {
|
||||||
_ = self.write(w);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Public for PublicKey {
|
|
||||||
type Error = PublicKeyError;
|
|
||||||
|
|
||||||
fn read(r: &mut encoding::Cursor) -> Result<Option<Self>, Self::Error> {
|
|
||||||
match r.read_string()? {
|
|
||||||
b"ssh-ed25519" => {
|
|
||||||
let s = r.read_string()?;
|
|
||||||
let p = PublicKey::try_from(s)?;
|
|
||||||
|
|
||||||
Ok(Some(p))
|
|
||||||
}
|
|
||||||
_ => Ok(None),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write<E: Encoding>(&self, buf: &mut E) -> usize {
|
|
||||||
let mut str_w: Vec<u8> = Vec::<u8>::new();
|
let mut str_w: Vec<u8> = Vec::<u8>::new();
|
||||||
str_w.extend_ssh_string(b"ssh-ed25519");
|
str_w.extend_ssh_string(b"ssh-ed25519");
|
||||||
str_w.extend_ssh_string(&self[..]);
|
str_w.extend_ssh_string(&self[..]);
|
||||||
buf.extend_ssh_string(&str_w)
|
w.extend_ssh_string(&str_w)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -188,14 +162,16 @@ pub enum SecretKeyError {
|
||||||
Crypto(#[from] crypto::Error),
|
Crypto(#[from] crypto::Error),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Io(#[from] io::Error),
|
Io(#[from] io::Error),
|
||||||
|
#[error("unknown algorithm '{0}'")]
|
||||||
|
UnknownAlgorithm(String),
|
||||||
#[error("public key does not match secret key")]
|
#[error("public key does not match secret key")]
|
||||||
Mismatch,
|
Mismatch,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl radicle_ssh::key::Private for SecretKey {
|
impl Encodable for SecretKey {
|
||||||
type Error = SecretKeyError;
|
type Error = SecretKeyError;
|
||||||
|
|
||||||
fn read(r: &mut encoding::Cursor) -> Result<Option<Self>, Self::Error> {
|
fn read(r: &mut encoding::Cursor) -> Result<Self, Self::Error> {
|
||||||
match r.read_string()? {
|
match r.read_string()? {
|
||||||
b"ssh-ed25519" => {
|
b"ssh-ed25519" => {
|
||||||
let public = r.read_string()?;
|
let public = r.read_string()?;
|
||||||
|
|
@ -206,38 +182,21 @@ impl radicle_ssh::key::Private for SecretKey {
|
||||||
if public != key.public_key().as_ref() {
|
if public != key.public_key().as_ref() {
|
||||||
return Err(SecretKeyError::Mismatch);
|
return Err(SecretKeyError::Mismatch);
|
||||||
}
|
}
|
||||||
Ok(Some(SecretKey(key)))
|
Ok(SecretKey(key))
|
||||||
}
|
}
|
||||||
_ => Ok(None),
|
s => Err(SecretKeyError::UnknownAlgorithm(
|
||||||
|
String::from_utf8_lossy(s).to_string(),
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write(&self, buf: &mut Zeroizing<Vec<u8>>) -> Result<(), Self::Error> {
|
fn write<E: Encoding>(&self, buf: &mut E) {
|
||||||
let public = self.0.public_key();
|
let public = self.0.public_key();
|
||||||
|
|
||||||
buf.extend_ssh_string(b"ssh-ed25519");
|
buf.extend_ssh_string(b"ssh-ed25519");
|
||||||
buf.extend_ssh_string(public.as_ref());
|
buf.extend_ssh_string(public.as_ref());
|
||||||
buf.deref_mut().write_u32::<BigEndian>(64)?;
|
buf.extend_ssh_string(&*self.0);
|
||||||
buf.extend(&*self.0);
|
|
||||||
buf.extend_ssh_string(b"radicle");
|
buf.extend_ssh_string(b"radicle");
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_signature<Bytes: AsRef<[u8]>>(
|
|
||||||
&self,
|
|
||||||
data: Bytes,
|
|
||||||
buf: &mut Zeroizing<Vec<u8>>,
|
|
||||||
) -> Result<(), Self::Error> {
|
|
||||||
let name = "ssh-ed25519";
|
|
||||||
let signature: [u8; 64] = *self.0.sign(data.as_ref(), None);
|
|
||||||
|
|
||||||
buf.deref_mut()
|
|
||||||
.write_u32::<BigEndian>((name.len() + signature.len() + 8) as u32)?;
|
|
||||||
buf.extend_ssh_string(name.as_bytes());
|
|
||||||
buf.extend_ssh_string(&signature);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -253,12 +212,8 @@ pub enum ExtendedSignatureError {
|
||||||
MissingHeader,
|
MissingHeader,
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Encoding(#[from] encoding::Error),
|
Encoding(#[from] encoding::Error),
|
||||||
#[error("public key encoding")]
|
|
||||||
PublicKeyEncoding,
|
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
PublicKeyError(#[from] PublicKeyError),
|
PublicKey(#[from] PublicKeyError),
|
||||||
#[error("signature encoding")]
|
|
||||||
SignatureEncoding,
|
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
SignatureError(#[from] SignatureError),
|
SignatureError(#[from] SignatureError),
|
||||||
#[error("unsupported version '{0}'")]
|
#[error("unsupported version '{0}'")]
|
||||||
|
|
@ -270,8 +225,8 @@ pub enum ExtendedSignatureError {
|
||||||
/// See <https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.sshsig>
|
/// See <https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.sshsig>
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct ExtendedSignature {
|
pub struct ExtendedSignature {
|
||||||
sig_version: u32,
|
version: u32,
|
||||||
publickey: crypto::PublicKey,
|
public_key: crypto::PublicKey,
|
||||||
/// Unambigious interpretation domain to prevent cross-protocol attacks.
|
/// Unambigious interpretation domain to prevent cross-protocol attacks.
|
||||||
namespace: Vec<u8>,
|
namespace: Vec<u8>,
|
||||||
reserved: Vec<u8>,
|
reserved: Vec<u8>,
|
||||||
|
|
@ -283,31 +238,30 @@ pub struct ExtendedSignature {
|
||||||
impl Encodable for ExtendedSignature {
|
impl Encodable for ExtendedSignature {
|
||||||
type Error = ExtendedSignatureError;
|
type Error = ExtendedSignatureError;
|
||||||
|
|
||||||
fn read_ssh(r: &mut encoding::Cursor) -> Result<Self, Self::Error> {
|
fn read(r: &mut encoding::Cursor) -> Result<Self, Self::Error> {
|
||||||
let sig_version = r.read_u32()?;
|
let sig_version = r.read_u32()?;
|
||||||
if sig_version > 1 {
|
if sig_version > 1 {
|
||||||
return Err(ExtendedSignatureError::UnsupportedVersion(sig_version));
|
return Err(ExtendedSignatureError::UnsupportedVersion(sig_version));
|
||||||
}
|
}
|
||||||
|
let mut pk = r.read_string()?.reader(0);
|
||||||
|
|
||||||
Ok(ExtendedSignature {
|
Ok(ExtendedSignature {
|
||||||
sig_version,
|
version: sig_version,
|
||||||
publickey: PublicKey::read_ssh(r)
|
public_key: PublicKey::read(&mut pk)?,
|
||||||
.map_err(|_| ExtendedSignatureError::PublicKeyEncoding)?,
|
|
||||||
namespace: r.read_string()?.into(),
|
namespace: r.read_string()?.into(),
|
||||||
reserved: r.read_string()?.into(),
|
reserved: r.read_string()?.into(),
|
||||||
hash_algorithm: r.read_string()?.into(),
|
hash_algorithm: r.read_string()?.into(),
|
||||||
signature: crypto::Signature::read_ssh(r)
|
signature: crypto::Signature::read(r)?,
|
||||||
.map_err(|_| ExtendedSignatureError::PublicKeyEncoding)?,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_ssh<E: Encoding>(&self, buf: &mut E) {
|
fn write<E: Encoding>(&self, buf: &mut E) {
|
||||||
buf.extend_u32(self.sig_version);
|
buf.extend_u32(self.version);
|
||||||
let _ = &self.publickey.write_ssh(buf);
|
let _ = &self.public_key.write(buf);
|
||||||
buf.extend_ssh_string(&self.namespace);
|
buf.extend_ssh_string(&self.namespace);
|
||||||
buf.extend_ssh_string(&self.reserved);
|
buf.extend_ssh_string(&self.reserved);
|
||||||
buf.extend_ssh_string(&self.hash_algorithm);
|
buf.extend_ssh_string(&self.hash_algorithm);
|
||||||
let _ = &self.signature.write_ssh(buf);
|
let _ = &self.signature.write(buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -334,27 +288,24 @@ impl ExtendedSignature {
|
||||||
return Err(ExtendedSignatureError::MagicPreamble(preamble));
|
return Err(ExtendedSignatureError::MagicPreamble(preamble));
|
||||||
}
|
}
|
||||||
|
|
||||||
let sig = ExtendedSignature::read_ssh(&mut reader)?;
|
let sig = ExtendedSignature::read(&mut reader)?;
|
||||||
Ok(sig)
|
Ok(sig)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_armored(&self) -> Vec<u8> {
|
pub fn to_armored(&self) -> Vec<u8> {
|
||||||
let mut v = encoding::Buffer::default();
|
let mut buf = encoding::Buffer::from(Self::MAGIC_PREAMBLE.to_vec());
|
||||||
|
self.write(&mut buf);
|
||||||
v.extend(Self::MAGIC_PREAMBLE);
|
|
||||||
self.write_ssh(&mut v);
|
|
||||||
|
|
||||||
let mut armored = Self::ARMORED_HEADER.to_vec();
|
let mut armored = Self::ARMORED_HEADER.to_vec();
|
||||||
armored.push(b'\n');
|
armored.push(b'\n');
|
||||||
|
|
||||||
let body: Vec<u8> = base64::encode(v).into();
|
let body = base64::encode(buf);
|
||||||
for line in body.chunks(Self::ARMORED_WIDTH) {
|
for line in body.as_bytes().chunks(Self::ARMORED_WIDTH) {
|
||||||
armored.extend(line.to_vec());
|
armored.extend(line);
|
||||||
armored.push(b'\n');
|
armored.push(b'\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
armored.extend(Self::ARMORED_FOOTER);
|
armored.extend(Self::ARMORED_FOOTER);
|
||||||
|
|
||||||
armored
|
armored
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -366,13 +317,12 @@ mod test {
|
||||||
use quickcheck_macros::quickcheck;
|
use quickcheck_macros::quickcheck;
|
||||||
use zeroize::Zeroizing;
|
use zeroize::Zeroizing;
|
||||||
|
|
||||||
use super::{ExtendedSignature, SecretKey};
|
use super::{fmt, ExtendedSignature, SecretKey};
|
||||||
use crate::crypto;
|
|
||||||
use crate::crypto::PublicKey;
|
use crate::crypto::PublicKey;
|
||||||
|
use crate::crypto::{self};
|
||||||
use crate::test::arbitrary::ByteArray;
|
use crate::test::arbitrary::ByteArray;
|
||||||
use radicle_ssh::agent::client::{AgentClient, ClientStream, Error};
|
use radicle_ssh::agent::client::{AgentClient, ClientStream, Error};
|
||||||
use radicle_ssh::encoding::Reader;
|
use radicle_ssh::encoding::*;
|
||||||
use radicle_ssh::key::Private as _;
|
|
||||||
|
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
struct DummyStream {
|
struct DummyStream {
|
||||||
|
|
@ -396,12 +346,12 @@ mod test {
|
||||||
|
|
||||||
#[quickcheck]
|
#[quickcheck]
|
||||||
fn prop_encode_decode_sk(input: ByteArray<64>) {
|
fn prop_encode_decode_sk(input: ByteArray<64>) {
|
||||||
let mut buf = Vec::new().into();
|
let mut buf = Buffer::default();
|
||||||
let sk = crypto::SecretKey::new(input.into_inner());
|
let sk = crypto::SecretKey::new(input.into_inner());
|
||||||
SecretKey(sk).write(&mut buf).unwrap();
|
SecretKey(sk).write(&mut buf);
|
||||||
|
|
||||||
let mut cursor = buf.reader(0);
|
let mut cursor = buf.reader(0);
|
||||||
let output = SecretKey::read(&mut cursor).unwrap().unwrap();
|
let output = SecretKey::read(&mut cursor).unwrap();
|
||||||
|
|
||||||
assert_eq!(sk, output.0);
|
assert_eq!(sk, output.0);
|
||||||
}
|
}
|
||||||
|
|
@ -469,16 +419,43 @@ mod test {
|
||||||
let armored: &[u8] = b"-----BEGIN SSH SIGNATURE-----
|
let armored: &[u8] = b"-----BEGIN SSH SIGNATURE-----
|
||||||
U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgvjrQogRxxLjzzWns8+mKJAGzEX
|
U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgvjrQogRxxLjzzWns8+mKJAGzEX
|
||||||
4fm2ALoN7pyvD2ttQAAAADZ2l0AAAAAAAAAAZzaGE1MTIAAABTAAAAC3NzaC1lZDI1NTE5
|
4fm2ALoN7pyvD2ttQAAAADZ2l0AAAAAAAAAAZzaGE1MTIAAABTAAAAC3NzaC1lZDI1NTE5
|
||||||
AAAAQJ759x+pFz0z2yM13S/sqeOOSgTE3fhoJG54dotNTk17dQEPKnH4S4N5jjA+pxM1mb
|
AAAAQI84aPZsXxlQigpy1/Y/iJSmHSS//CIgvqvUMQIb/TM2vhCKruduH0cK02k9G8wOI+
|
||||||
oejZ0WJ0cQtBjWZ7JEBQM=
|
EUMf2bSDyxbJyZThOEiAs=
|
||||||
-----END SSH SIGNATURE-----";
|
-----END SSH SIGNATURE-----";
|
||||||
|
|
||||||
|
let public_key = "AAAAC3NzaC1lZDI1NTE5AAAAIL460KIEccS4881p7PPpiiQBsxF+H5tgC6De6crw9rbU";
|
||||||
let signature = ExtendedSignature::from_armored(armored).unwrap();
|
let signature = ExtendedSignature::from_armored(armored).unwrap();
|
||||||
assert_eq!(1, signature.sig_version);
|
|
||||||
|
assert_eq!(signature.version, 1);
|
||||||
|
assert_eq!(fmt::key(&signature.public_key), public_key);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
String::from_utf8(armored.to_vec()),
|
String::from_utf8(armored.to_vec()),
|
||||||
String::from_utf8(signature.to_armored()),
|
String::from_utf8(signature.to_armored()),
|
||||||
"signature should remain unaltered after decoding"
|
"signature should remain unaltered after decoding"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_signature_verify() {
|
||||||
|
let seed = crypto::Seed::new([1; 32]);
|
||||||
|
let pair = crypto::KeyPair::from_seed(seed);
|
||||||
|
let message = &[0xff];
|
||||||
|
let sig = pair.sk.sign(message, None);
|
||||||
|
let esig = ExtendedSignature {
|
||||||
|
version: 1,
|
||||||
|
public_key: pair.pk.into(),
|
||||||
|
signature: sig.into(),
|
||||||
|
hash_algorithm: vec![],
|
||||||
|
namespace: vec![],
|
||||||
|
reserved: vec![],
|
||||||
|
};
|
||||||
|
|
||||||
|
let armored = esig.to_armored();
|
||||||
|
let unarmored = ExtendedSignature::from_armored(&armored).unwrap();
|
||||||
|
|
||||||
|
unarmored
|
||||||
|
.public_key
|
||||||
|
.verify(message, &unarmored.signature)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue