Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
redox-os
pkgar
Commits
081ecf8a
Commit
081ecf8a
authored
Aug 22, 2020
by
Jeremy Soller
Browse files
Merge branch 'keys-errors' into 'master'
pkgar-keys errors See merge request
!7
parents
0bf77ce0
b36b0647
Changes
7
Hide whitespace changes
Inline
Side-by-side
pkgar-core/Cargo.toml
View file @
081ecf8a
...
...
@@ -8,7 +8,7 @@ repository = "https://gitlab.redox-os.org/redox-os/pkgar"
edition
=
"2018"
[dependencies]
blake3
=
{
version
=
"0.3.
5
"
,
default_features
=
false
,
features
=
["rayon"]
}
blake3
=
{
version
=
"0.3.
6
"
,
default_features
=
false
,
features
=
["rayon"]
}
plain
=
"0.2.3"
sodiumoxide
=
{
version
=
"0.2.
5
"
,
default_features
=
false
}
sodiumoxide
=
{
version
=
"0.2.
6
"
,
default_features
=
false
}
pkgar-keys/Cargo.toml
View file @
081ecf8a
[package]
name
=
"pkgar-keys"
version
=
"0.1.0"
description
=
"Key management tool/library for pkgar"
license
=
"MIT"
authors
=
[
"Wesley Hershberger <mggmugginsmc@gmail.com>"
]
repository
=
"https://gitlab.redox-os.org/redox-os/pkgar"
keywords
=
[
"cli"
,
"redox"
,
"linux"
,
"cryptography"
,
"tool"
,
"pkgar"
]
readme
=
"README.md"
edition
=
"2018"
[dependencies]
clap
=
"2.33.
1
"
clap
=
"2.33.
3
"
dirs
=
"3.0.1"
hex
=
{
version
=
"0.4.2"
,
features
=
["serde"]
}
lazy_static
=
"1.4.0"
seckey
=
"0.
9.3
"
serde
=
{
version
=
"1.0.11
4
"
,
default_features
=
false
,
features
=
["derive"]
}
sodiumoxide
=
{
version
=
"0.2.
5
"
,
default_features
=
false
}
seckey
=
"0.
11.2
"
serde
=
{
version
=
"1.0.11
5
"
,
default_features
=
false
,
features
=
["derive"]
}
sodiumoxide
=
{
version
=
"0.2.
6
"
,
default_features
=
false
}
termion
=
"1.5.5"
thiserror
=
"1.0.20"
toml
=
"0.5.6"
user-error
=
"1.2.8"
pkgar-keys/README.md
View file @
081ecf8a
# `pkgar-keys`
Key management for pkgar.
Key management
tool/library
for pkgar.
Run the binary with
`--help`
for CLI documentation. Secret keys are stored in
`~/.pkgar/keys`
by default.
Keys are stored in toml format, the key format is subject to change.
Run the binary with
`--help`
for CLI documentation. Keys are stored in
`~/.pkgar/keys`
by default and are stored as toml. The key format is subject
to change.
pkgar-keys/src/error.rs
View file @
081ecf8a
use
std
::
error
::
Error
as
StdError
;
use
std
::
fmt
::{
self
,
Display
};
use
std
::
io
;
use
std
::
path
::
PathBuf
;
#[derive(Debug)]
pub
struct
FileError
{
kind
:
Error
,
src
:
PathBuf
,
}
impl
Display
for
FileError
{
fn
fmt
(
&
self
,
f
:
&
mut
fmt
::
Formatter
)
->
fmt
::
Result
{
write!
(
f
,
"{}: {}"
,
self
.src
.display
(),
self
.kind
)
}
}
impl
StdError
for
FileError
{
fn
source
(
&
self
)
->
Option
<&
(
dyn
StdError
+
'static
)
>
{
self
.kind
.source
()
}
}
#[derive(Debug)]
pub
enum
Error
{
Custom
(
String
),
Io
(
io
::
Error
),
use
user_error
::
UFE
;
use
thiserror
::
Error
;
/// An error which includes path context and implements `UFE` for easy display.
#[derive(Debug,
Error)]
#[error(
"File: {path}"
)]
pub
struct
Error
{
#[source]
pub
src
:
ErrorKind
,
pub
path
:
PathBuf
,
}
impl
UFE
for
Error
{}
/// The main error type that is used by this library internally. For additional
/// contextual information, most public routines use [`Error`](struct.Error.html).
#[non_exhaustive]
#[derive(Debug,
Error)]
pub
enum
ErrorKind
{
#[error(
"Io"
)]
Io
(
#[from]
io
::
Error
),
#[error(
"Key length invalid"
)]
KeyInvalid
,
#[error(
"Public and secret keys do not match"
)]
KeyMismatch
,
MAlloc
,
#[error(
"Invalid nonce length"
)]
NonceInvalid
,
#[error(
"Incorrect passphrase"
)]
PassphraseIncorrect
,
#[error(
"Passphrases did not match"
)]
PassphraseMismatch
,
Ser
(
toml
::
ser
::
Error
),
Deser
(
toml
::
de
::
Error
),
}
impl
From
<
io
::
Error
>
for
Error
{
fn
from
(
err
:
io
::
Error
)
->
Error
{
Error
::
Io
(
err
)
}
}
impl
From
<
toml
::
de
::
Error
>
for
Error
{
fn
from
(
err
:
toml
::
de
::
Error
)
->
Error
{
Error
::
Deser
(
err
)
}
}
impl
From
<
toml
::
ser
::
Error
>
for
Error
{
fn
from
(
err
:
toml
::
ser
::
Error
)
->
Error
{
Error
::
Ser
(
err
)
}
}
impl
Display
for
Error
{
fn
fmt
(
&
self
,
f
:
&
mut
fmt
::
Formatter
)
->
fmt
::
Result
{
let
msg
=
match
self
{
Error
::
Custom
(
e
)
=>
e
.clone
(),
Error
::
KeyInvalid
=>
"Key length invalid"
.to_string
(),
Error
::
KeyMismatch
=>
"Public and private keys do not match"
.to_string
(),
Error
::
MAlloc
=>
"Unable to allocate locked/zeroed memory"
.to_string
(),
Error
::
NonceInvalid
=>
"Nonce length invalid"
.to_string
(),
Error
::
PassphraseIncorrect
=>
"Incorrect passphrase"
.to_string
(),
Error
::
PassphraseMismatch
=>
"Passphrases do not match"
.to_string
(),
Error
::
Io
(
err
)
=>
format!
(
"{}"
,
err
),
Error
::
Ser
(
err
)
=>
format!
(
"{}"
,
err
),
Error
::
Deser
(
err
)
=>
format!
(
"{}"
,
err
),
};
write!
(
f
,
"Error: {}"
,
msg
)
}
}
impl
StdError
for
Error
{
fn
source
(
&
self
)
->
Option
<&
(
dyn
StdError
+
'static
)
>
{
match
self
{
Error
::
Io
(
err
)
=>
Some
(
err
),
Error
::
Ser
(
err
)
=>
Some
(
err
),
Error
::
Deser
(
err
)
=>
Some
(
err
),
_
=>
None
,
}
}
#[error(
"Serialization"
)]
Ser
(
#[from]
toml
::
ser
::
Error
),
#[error(
"Deserialization"
)]
Deser
(
#[from]
toml
::
de
::
Error
),
}
pkgar-keys/src/lib.rs
View file @
081ecf8a
mod
error
;
use
std
::
fs
::
File
;
use
std
::
io
::{
self
,
Read
,
stdin
,
stdout
,
Write
};
use
std
::
fs
::
{
self
,
File
,
OpenOptions
}
;
use
std
::
io
::{
self
,
stdin
,
stdout
,
Write
};
use
std
::
ops
::
Deref
;
use
std
::
os
::
unix
::
fs
::
OpenOptionsExt
;
use
std
::
path
::{
Path
,
PathBuf
};
use
hex
::
FromHex
;
use
lazy_static
::
lazy_static
;
use
seckey
::
Sec
Key
;
use
seckey
::
Sec
Bytes
;
use
serde
::{
Deserialize
,
Serialize
};
use
sodiumoxide
::
crypto
::{
pwhash
,
...
...
@@ -16,18 +17,28 @@ use sodiumoxide::crypto::{
};
use
termion
::
input
::
TermRead
;
pub
use
error
::
Error
;
pub
use
error
::
{
Error
Kind
,
Error
}
;
lazy_static!
{
static
ref
HOMEDIR
:
PathBuf
=
{
dirs
::
home_dir
()
.unwrap_or
(
"./"
.into
())
};
/// The default location for pkgar to look for the user's public key.
///
/// Defaults to `$HOME/.pkgar/keys/id_ed25519.pub.toml`. If `$HOME` is
/// unset, `./.pkgar/keys/id_ed25519.pub.toml`.
pub
static
ref
DEFAULT_PUBKEY
:
PathBuf
=
{
Path
::
join
(
&
HOMEDIR
,
".pkgar/keys/id_ed25519.toml"
)
Path
::
join
(
&
HOMEDIR
,
".pkgar/keys/id_ed25519.
pub.
toml"
)
};
/// The default location for pkgar to look for the user's secret key.
///
/// Defaults to `$HOME/.pkgar/keys/id_ed25519.toml`. If `$HOME` is unset,
/// `./.pkgar/keys/id_ed25519.toml`.
pub
static
ref
DEFAULT_SECKEY
:
PathBuf
=
{
Path
::
join
(
&
HOMEDIR
,
".pkgar/keys/id_ed25519.
pub.
toml"
)
Path
::
join
(
&
HOMEDIR
,
".pkgar/keys/id_ed25519.toml"
)
};
}
...
...
@@ -70,21 +81,40 @@ pub struct PublicKeyFile {
}
impl
PublicKeyFile
{
///
Helper function to deserialize
.
///
Parse a `PublicKeyFile` from `file` (in toml format)
.
pub
fn
open
(
file
:
&
Path
)
->
Result
<
PublicKeyFile
,
Error
>
{
let
mut
s
=
String
::
new
();
File
::
open
(
file
)
?
.read_to_string
(
&
mut
s
)
?
;
let
content
=
fs
::
read_to_string
(
file
)
.map_err
(|
src
|
Error
{
path
:
file
.to_path_buf
(),
src
:
ErrorKind
::
from
(
src
),
})
?
;
Ok
(
toml
::
from_str
(
&
s
)
?
)
toml
::
from_str
(
&
content
)
.map_err
(|
src
|
Error
{
path
:
file
.to_path_buf
(),
src
:
ErrorKind
::
from
(
src
),
})
}
/// Helper function to serialize and save.
pub
fn
save
(
&
self
,
file
:
&
Path
)
->
Result
<
(),
Error
>
{
File
::
create
(
file
)
?
.write_all
(
toml
::
to_string
(
self
)
?
.as_bytes
())
?
;
/// Write `self` serialized as toml to `w`.
pub
fn
write
(
&
self
,
mut
w
:
impl
Write
)
->
Result
<
(),
ErrorKind
>
{
w
.write_all
(
toml
::
to_string
(
self
)
?
.as_bytes
())
?
;
Ok
(())
}
/// Shortcut to write the public key to `file`
pub
fn
save
(
&
self
,
file
:
&
Path
)
->
Result
<
(),
Error
>
{
self
.write
(
File
::
create
(
file
)
.map_err
(|
src
|
Error
{
path
:
file
.to_path_buf
(),
src
:
ErrorKind
::
from
(
src
)
})
?
)
.map_err
(|
src
|
Error
{
path
:
file
.to_path_buf
(),
src
,
})
}
}
enum
SKey
{
...
...
@@ -93,9 +123,9 @@ enum SKey {
}
impl
SKey
{
fn
encrypt
(
&
mut
self
,
passwd
:
SecKey
<
str
>
,
salt
:
pwhash
::
Salt
,
nonce
:
secretbox
::
Nonce
)
{
fn
encrypt
(
&
mut
self
,
passwd
:
Passwd
,
salt
:
pwhash
::
Salt
,
nonce
:
secretbox
::
Nonce
)
{
if
let
SKey
::
Plain
(
skey
)
=
self
{
if
let
Some
(
passwd_key
)
=
gen_key
(
passwd
,
salt
)
{
if
let
Some
(
passwd_key
)
=
passwd
.
gen_key
(
salt
)
{
let
mut
buf
=
[
0
;
80
];
buf
.copy_from_slice
(
&
secretbox
::
seal
(
skey
.as_ref
(),
&
nonce
,
&
passwd_key
));
*
self
=
SKey
::
Cipher
(
buf
);
...
...
@@ -103,17 +133,17 @@ impl SKey {
}
}
fn
decrypt
(
&
mut
self
,
passwd
:
SecKey
<
str
>
,
salt
:
pwhash
::
Salt
,
nonce
:
secretbox
::
Nonce
)
->
Result
<
(),
Error
>
{
fn
decrypt
(
&
mut
self
,
passwd
:
Passwd
,
salt
:
pwhash
::
Salt
,
nonce
:
secretbox
::
Nonce
)
->
Result
<
(),
Error
Kind
>
{
if
let
SKey
::
Cipher
(
ciphertext
)
=
self
{
if
let
Some
(
passwd_key
)
=
gen_key
(
passwd
,
salt
)
{
if
let
Some
(
passwd_key
)
=
passwd
.
gen_key
(
salt
)
{
let
skey_plain
=
secretbox
::
open
(
ciphertext
.as_ref
(),
&
nonce
,
&
passwd_key
)
.map_err
(|
_
|
Error
::
PassphraseIncorrect
)
?
;
.map_err
(|
_
|
Error
Kind
::
PassphraseIncorrect
)
?
;
*
self
=
SKey
::
Plain
(
sign
::
SecretKey
::
from_slice
(
&
skey_plain
)
.ok_or
(
Error
::
KeyInvalid
)
?
);
.ok_or
(
Error
Kind
::
KeyInvalid
)
?
);
}
else
{
*
self
=
SKey
::
Plain
(
sign
::
SecretKey
::
from_slice
(
&
ciphertext
[
..
64
])
.ok_or
(
Error
::
KeyInvalid
)
?
);
.ok_or
(
Error
Kind
::
KeyInvalid
)
?
);
}
}
Ok
(())
...
...
@@ -146,7 +176,7 @@ impl FromHex for SKey {
// Public key is only 64 bytes...
if
bytes
.len
()
==
64
{
Ok
(
SKey
::
Plain
(
sign
::
SecretKey
::
from_slice
(
&
bytes
)
.expect
(
"Somehow not the right number of bytes"
)))
.expect
(
"Somehow not the right number of bytes"
)))
}
else
{
let
mut
buf
=
[
0
;
80
];
buf
.copy_from_slice
(
&
bytes
);
...
...
@@ -169,8 +199,8 @@ pub struct SecretKeyFile {
}
impl
SecretKeyFile
{
/// Generate a keypair with all the nessesary info to save both
///
keys. You
must call `save()` on each object to persist to disk.
/// Generate a keypair with all the nessesary info to save both
keys. You
/// must call `save()` on each object to persist
them
to disk.
pub
fn
new
()
->
(
PublicKeyFile
,
SecretKeyFile
)
{
let
(
pkey
,
skey
)
=
sign
::
gen_keypair
();
...
...
@@ -184,33 +214,57 @@ impl SecretKeyFile {
(
pkey_file
,
skey_file
)
}
/// Parse a SecretKeyFile from `file`.
/// Parse a
`
SecretKeyFile
`
from `file`
(in toml format)
.
pub
fn
open
(
file
:
&
Path
)
->
Result
<
SecretKeyFile
,
Error
>
{
let
mut
s
=
String
::
new
();
File
::
open
(
file
)
?
.read_to_string
(
&
mut
s
)
?
;
let
content
=
fs
::
read_to_string
(
file
)
.map_err
(|
src
|
Error
{
path
:
file
.to_path_buf
(),
src
:
ErrorKind
::
Io
(
src
),
})
?
;
Ok
(
toml
::
from_str
(
&
s
)
?
)
toml
::
from_str
(
&
content
)
.map_err
(|
src
|
Error
{
path
:
file
.to_path_buf
(),
src
:
ErrorKind
::
Deser
(
src
),
})
}
/// Write `self` serialized as toml to `w`.
pub
fn
write
(
&
self
,
mut
w
:
impl
Write
)
->
Result
<
(),
ErrorKind
>
{
w
.write_all
(
toml
::
to_string
(
&
self
)
?
.as_bytes
())
?
;
Ok
(())
}
/// Save the secret key to `file`.
/// Shortcut to write the secret key to `file`.
///
/// Make sure to call `encrypt()` in order to encrypt
/// the private key, otherwise it will be stored as plain text.
pub
fn
save
(
&
self
,
file
:
&
Path
)
->
Result
<
(),
Error
>
{
File
::
create
(
file
)
?
.write_all
(
toml
::
to_string
(
&
self
)
?
.as_bytes
())
?
;
Ok
(())
self
.write
(
OpenOptions
::
new
()
.write
(
true
)
.create
(
true
)
.mode
(
0o600
)
.open
(
file
)
.map_err
(|
src
|
Error
{
path
:
file
.to_path_buf
(),
src
:
ErrorKind
::
from
(
src
),
})
?
)
.map_err
(|
src
|
Error
{
path
:
file
.to_path_buf
(),
src
,
})
}
/// Ensure that the internal state of this struct is encrypted.
/// Note that if passwd is empty, this function is a no-op.
pub
fn
encrypt
(
&
mut
self
,
passwd
:
SecKey
<
str
>
)
{
pub
fn
encrypt
(
&
mut
self
,
passwd
:
Passwd
)
{
self
.skey
.encrypt
(
passwd
,
self
.salt
,
self
.nonce
)
}
/// Ensure that the internal state of this struct is decrypted.
/// If the internal state is already decrypted, this function is a no-op.
pub
fn
decrypt
(
&
mut
self
,
passwd
:
SecKey
<
str
>
)
->
Result
<
(),
Error
>
{
pub
fn
decrypt
(
&
mut
self
,
passwd
:
Passwd
)
->
Result
<
(),
Error
Kind
>
{
self
.skey
.decrypt
(
passwd
,
self
.salt
,
self
.nonce
)
}
...
...
@@ -238,62 +292,101 @@ impl SecretKeyFile {
}
}
/// Get a key for symmetric key encryption from a password.
fn
gen_key
(
passwd
:
SecKey
<
str
>
,
salt
:
pwhash
::
Salt
)
->
Option
<
secretbox
::
Key
>
{
if
passwd
.read
()
.deref
()
==
""
{
None
}
else
{
let
mut
key
=
secretbox
::
Key
([
0
;
secretbox
::
KEYBYTES
]);
let
secretbox
::
Key
(
ref
mut
binary_key
)
=
key
;
pwhash
::
derive_key
(
binary_key
,
passwd
.read
()
.as_bytes
(),
&
salt
,
pwhash
::
OPSLIMIT_INTERACTIVE
,
pwhash
::
MEMLIMIT_INTERACTIVE
)
.expect
(
"Failed to get key from password"
);
Some
(
key
)
}
/// Secure in-memory representation of a password.
pub
struct
Passwd
{
bytes
:
SecBytes
,
}
/// Prompt the user for a password on stdin.
fn
get_passwd
(
prompt
:
&
str
)
->
Result
<
SecKey
<
str
>
,
Error
>
{
let
stdout
=
stdout
();
let
mut
stdout
=
stdout
.lock
();
let
stdin
=
stdin
();
let
mut
stdin
=
stdin
.lock
();
stdout
.write_all
(
prompt
.as_bytes
())
?
;
stdout
.flush
()
?
;
impl
Passwd
{
/// Create a new `Passwd` and zero the old string.
pub
fn
new
(
passwd
:
&
mut
String
)
->
Passwd
{
let
pwd
=
Passwd
{
bytes
:
SecBytes
::
with
(
passwd
.len
(),
|
buf
|
buf
.copy_from_slice
(
passwd
.as_bytes
())
),
};
unsafe
{
seckey
::
zero
(
passwd
.as_bytes_mut
());
}
pwd
}
let
mut
passwd
=
stdin
.read_passwd
(
&
mut
stdout
)
?
.ok_or
(
Error
::
Io
(
io
::
Error
::
new
(
io
::
ErrorKind
::
UnexpectedEof
,
"Invalid Password Input"
)))
?
;
/// Prompt the user for a `Passwd` on stdin.
pub
fn
prompt
(
prompt
:
impl
AsRef
<
str
>
)
->
Result
<
Passwd
,
ErrorKind
>
{
let
stdout
=
stdout
();
let
mut
stdout
=
stdout
.lock
();
let
stdin
=
stdin
();
let
mut
stdin
=
stdin
.lock
();
stdout
.write_all
(
prompt
.as_ref
()
.as_bytes
())
?
;
stdout
.flush
()
?
;
let
mut
passwd
=
stdin
.read_passwd
(
&
mut
stdout
)
?
.ok_or
(
ErrorKind
::
Io
(
io
::
Error
::
new
(
io
::
ErrorKind
::
UnexpectedEof
,
"Invalid Password Input"
,
)
))
?
;
println!
();
Ok
(
Passwd
::
new
(
&
mut
passwd
))
}
let
passwd
=
SecKey
::
from_str
(
&
mut
passwd
)
.ok_or
(
Error
::
MAlloc
)
?
;
/// Prompt for a password on stdin and confirm it. For configurable
/// prompts, use [`Passwd::prompt`](struct.Passwd.html#method.prompt).
pub
fn
prompt_new
()
->
Result
<
Passwd
,
ErrorKind
>
{
let
passwd
=
Passwd
::
prompt
(
"Please enter a new passphrase (leave empty to store the key in plaintext): "
)
?
;
let
confirm
=
Passwd
::
prompt
(
"Please re-enter the passphrase: "
)
?
;
println!
();
if
passwd
!=
confirm
{
Err
(
ErrorKind
::
PassphraseMismatch
)
}
else
{
Ok
(
passwd
)
}
}
Ok
(
passwd
)
/// Get a key for symmetric key encryption from a password.
fn
gen_key
(
&
self
,
salt
:
pwhash
::
Salt
)
->
Option
<
secretbox
::
Key
>
{
if
self
.bytes
.read
()
.len
()
>
0
{
let
mut
key
=
secretbox
::
Key
([
0
;
secretbox
::
KEYBYTES
]);
let
secretbox
::
Key
(
ref
mut
binary_key
)
=
key
;
pwhash
::
derive_key
(
binary_key
,
&
self
.bytes
.read
(),
&
salt
,
pwhash
::
OPSLIMIT_INTERACTIVE
,
pwhash
::
MEMLIMIT_INTERACTIVE
,
)
.expect
(
"Failed to get key from password"
);
Some
(
key
)
}
else
{
None
}
}
}
/// Prompt for a password and confirm it.
fn
get_new_passwd
()
->
Result
<
SecKey
<
str
>
,
Error
>
{
let
passwd
=
get_passwd
(
"Please enter a new passphrase (leave empty to store the key in plaintext): "
)
?
;
let
confirm
=
get_passwd
(
"Please re-enter the passphrase: "
)
?
;
if
passwd
.read
()
.deref
()
!=
confirm
.read
()
.deref
()
{
Err
(
Error
::
PassphraseMismatch
)
}
else
{
Ok
(
passwd
)
impl
PartialEq
for
Passwd
{
fn
eq
(
&
self
,
other
:
&
Passwd
)
->
bool
{
self
.bytes
.read
()
.deref
()
==
other
.bytes
.read
()
.deref
()
}
}
impl
Eq
for
Passwd
{}
/// Generate a new keypair. The new keys will be saved to `file`. The user
/// will be prompted on stdin for a password, empty passwords will cause the
/// secret key to be stored in plain text. Note that parent
/// directories will not be created.
pub
fn
gen_keypair
(
pkey_path
:
&
Path
,
skey_path
:
&
Path
)
->
Result
<
(
PublicKeyFile
,
SecretKeyFile
),
Error
>
{
let
passwd
=
get_new_passwd
()
?
;
let
passwd
=
Passwd
::
prompt_new
()
.map_err
(|
src
|
Error
{
path
:
skey_path
.to_path_buf
(),
src
,
})
?
;
let
(
pkey_file
,
mut
skey_file
)
=
SecretKeyFile
::
new
();
skey_file
.encrypt
(
passwd
);
...
...
@@ -305,29 +398,39 @@ pub fn gen_keypair(pkey_path: &Path, skey_path: &Path) -> Result<(PublicKeyFile,
Ok
((
pkey_file
,
skey_file
))
}
/// Get a SecretKeyFile from a path. If the file is encrypted, prompt for a password on stdin.
pub
fn
get_skey
(
skey_path
:
&
Path
)
->
Result
<
SecretKeyFile
,
Error
>
{
fn
prompt_skey
(
skey_path
:
&
Path
,
prompt
:
impl
AsRef
<
str
>
)
->
Result
<
SecretKeyFile
,
Error
>
{
let
to_file_err
=
|
src
|
Error
{
path
:
skey_path
.to_path_buf
(),
src
,
};
let
mut
key_file
=
SecretKeyFile
::
open
(
skey_path
)
?
;
if
key_file
.is_encrypted
()
{
let
passwd
=
get_passwd
(
&
format!
(
"Passphrase for {}: "
,
skey_path
.display
()))
?
;
key_file
.decrypt
(
passwd
)
?
;
let
passwd
=
Passwd
::
prompt
(
&
format!
(
"{} {}: "
,
prompt
.as_ref
(),
skey_path
.display
()))
.map_err
(
to_file_err
)
?
;
key_file
.decrypt
(
passwd
)
.map_err
(
to_file_err
)
?
;
}
Ok
(
key_file
)
}
/// Get a SecretKeyFile from a path. If the file is encrypted, prompt for a password on stdin.
pub
fn
get_skey
(
skey_path
:
&
Path
)
->
Result
<
SecretKeyFile
,
Error
>
{
prompt_skey
(
skey_path
,
"Passphrase for"
)
}
/// Open, decrypt, re-encrypt with a different passphrase from stdin, and save the newly encrypted
/// secret key at `skey_path`.
pub
fn
re_encrypt
(
skey_path
:
&
Path
)
->
Result
<
(),
Error
>
{
let
mut
skey_file
=
SecretKeyFile
::
open
(
skey_path
)
?
;
if
skey_file
.is_encrypted
()
{
let
passwd
=
get_passwd
(
&
format!
(
"Old passphrase for {}: "
,
skey_path
.display
()))
?
;
skey_file
.decrypt
(
passwd
)
?
;
}
let
mut
skey_file
=
prompt_skey
(
skey_path
,
"Old passphrase for"
)
?
;
let
passwd
=
get_new_passwd
()
?
;
let
passwd
=
Passwd
::
prompt_new
()
.map_err
(|
src
|
Error
{
path
:
skey_path
.to_path_buf
(),
src
,
})
?
;
skey_file
.encrypt
(
passwd
);
skey_file
.save
(
skey_path
)
...
...
pkgar-keys/src/main.rs
View file @
081ecf8a
use
std
::
io
::{
stdout
,
Write
}
;
use
std
::
fs
::{
self
,
File
}
;
use
std
::
io
;
use
std
::
fs
;
use
std
::
path
::
PathBuf
;
use
std
::
process
;
use
clap
::
clap_app
;
use
user_error
::
UFE
;
use
pkgar_keys
::{
DEFAULT_PUBKEY
,
DEFAULT_SECKEY
,
Error
,
ErrorKind
,
gen_keypair
,
get_skey
,
SecretKeyFile
,
...
...
@@ -31,10 +33,10 @@ fn cli() -> Result<i32, Error> {
"Don't check for existing files before generating a new keypair"
)
)