Compare commits
No commits in common. "master" and "v0.5.0" have entirely different histories.
37 changed files with 2786 additions and 4194 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -24,11 +24,12 @@ _testmain.go
|
||||||
*.prof
|
*.prof
|
||||||
vendor/*
|
vendor/*
|
||||||
|
|
||||||
|
# vendor management
|
||||||
|
vendor/src/*
|
||||||
|
vendor/pkg/*
|
||||||
bin/*
|
bin/*
|
||||||
sigtool
|
|
||||||
|
|
||||||
.??*.sw?
|
.??*.sw?
|
||||||
*.pub
|
*.pub
|
||||||
*.key
|
*.key
|
||||||
*.sig
|
*.sig
|
||||||
releases/*
|
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
[tools]
|
|
||||||
golang = "1.24"
|
|
197
README.md
197
README.md
|
@ -7,40 +7,28 @@
|
||||||
`sigtool` is an opinionated tool to generate keys, sign, verify, encrypt &
|
`sigtool` is an opinionated tool to generate keys, sign, verify, encrypt &
|
||||||
decrypt files using Ed25519 signature scheme. In many ways, it is like
|
decrypt files using Ed25519 signature scheme. In many ways, it is like
|
||||||
like OpenBSD's [signify][1] -- except written in Golang and definitely
|
like OpenBSD's [signify][1] -- except written in Golang and definitely
|
||||||
easier to use. It can use SSH ed25519 public and private keys.
|
easier to use.
|
||||||
|
|
||||||
It can sign and verify very large files - it prehashes the files
|
It can sign and verify very large files - it prehashes the files
|
||||||
with SHA-512 and then signs the SHA-512 checksum. The keys and signatures
|
with SHA-512 and then signs the SHA-512 checksum. The keys and signatures
|
||||||
are human readable YAML files.
|
are YAML files and so, human readable.
|
||||||
|
|
||||||
It can encrypt files for multiple recipients - each of whom is identified
|
It can encrypt files for multiple recipients - each of whom is identified
|
||||||
by their Ed25519 public key. The encryption generates ephmeral
|
by their Ed25519 public key. The encryption by default generates ephmeral
|
||||||
Curve25519 keys and creates pair-wise shared secret for each recipient of
|
Curve25519 keys and creates pair-wise shared secret for each recipient of
|
||||||
the encrypted file. The caller can optionally use a specific private key
|
the encrypted file. The caller can optionally use a specific secret key
|
||||||
during the encryption process - this has the benefit of also authenticating
|
during the encryption process - this has the benefit of also authenticating
|
||||||
the sender (and the receiver can verify the sender if they possess the
|
the sender (and the receiver can verify the sender if they possess the
|
||||||
corresponding sender's public key).
|
corresponding public key).
|
||||||
|
|
||||||
The sign, verify, encrypt, decrypt operations can use OpenSSH Ed25519 keys
|
The sign, verify, encrypt, decrypt operations can use OpenSSH Ed25519 keys
|
||||||
*or* the keys generated by sigtool. This means, you can send encrypted
|
*or* the keys generated by sigtool. This means, you can send encrypted
|
||||||
files to any recipient identified by their comment in `~/.ssh/authorized_keys`.
|
files to any recipient identified by their comment in `~/.ssh/authorized_keys`.
|
||||||
|
|
||||||
## How do I build it?
|
## How do I build it?
|
||||||
You need two things:
|
With Go 1.5 and later:
|
||||||
|
|
||||||
1. Protobuf compiler:
|
git clone https://github.com/opencoff/sigtool
|
||||||
|
|
||||||
On Debian based systems: `apt install protobuf-compiler`
|
|
||||||
|
|
||||||
Consult your OS's package manager to install protobuf tools;
|
|
||||||
these are typically named 'protobuf' or 'protoc'.
|
|
||||||
|
|
||||||
2. go 1.13+ toolchain
|
|
||||||
|
|
||||||
|
|
||||||
Next, build sigtool:
|
|
||||||
|
|
||||||
git clone https://git.rgst.io/homelab/sigtool/v3
|
|
||||||
cd sigtool
|
cd sigtool
|
||||||
make
|
make
|
||||||
|
|
||||||
|
@ -101,10 +89,6 @@ e.g., to verify the signature of *archive.tar.gz* against
|
||||||
sigtool verify /tmp/testkey.pub archive.sig archive.tar.gz
|
sigtool verify /tmp/testkey.pub archive.sig archive.tar.gz
|
||||||
|
|
||||||
|
|
||||||
You can also pass a public key as a string (instead of a file):
|
|
||||||
|
|
||||||
sigtool verify iF84Dymq/bAEnUMK6DRIHWAQDRD8FwDDDfsgFfzdjWM= archive.sig archive.tar.gz
|
|
||||||
|
|
||||||
Note that signing and verifying can also work with OpenSSH ed25519
|
Note that signing and verifying can also work with OpenSSH ed25519
|
||||||
keys.
|
keys.
|
||||||
|
|
||||||
|
@ -154,136 +138,80 @@ recipient can decrypt using their private key.
|
||||||
|
|
||||||
## Technical Details
|
## Technical Details
|
||||||
|
|
||||||
### How is the file encryption done?
|
### How is the private key protected?
|
||||||
|
The Ed25519 private key is encrypted using a key derived from the
|
||||||
|
user supplied pass phrase. This pass phrase is used to derive an
|
||||||
|
encryption key using the Scrypt key derivation algorithm. The
|
||||||
|
resulting derived key is XOR'd with the Ed25519 private key before
|
||||||
|
being committed to disk. To protect the integrity of the process,
|
||||||
|
the essential parameters used for deriving the key, and the derived
|
||||||
|
key are hashed via SHA256 and stored along with the encrypted key.
|
||||||
|
|
||||||
|
As an additional security measure, the user supplied pass phrase is
|
||||||
|
hashed with SHA512.
|
||||||
|
|
||||||
|
### How is the Encryption done?
|
||||||
The file encryption uses AES-GCM-256 in AEAD mode. The encryption uses
|
The file encryption uses AES-GCM-256 in AEAD mode. The encryption uses
|
||||||
a random 32-byte AES-256 key. This root key is expanded via
|
a random 32-byte AES-256 key. The input is broken into chunks and
|
||||||
HKDF-SHA256 into:
|
each chunk is individually AEAD encrypted. The default chunk size
|
||||||
|
is 4MB (4 * 1048576 bytes). Each chunk generates its own nonce
|
||||||
|
from a global salt. The nonce is calculated as a SHA256 hash of
|
||||||
|
the salt, the chunk length and the block number.
|
||||||
|
|
||||||
- AES-GCM-256 key (32 bytes)
|
### What is the public-key cryptography used?
|
||||||
- AES Nonce (12 bytes)
|
`sigtool` uses Curve25519 ECC to generate shared secrets between
|
||||||
- HMAC-SHA-256 key (32 bytes)
|
pairs of sender & recipients. This pairwise shared secret is expanded
|
||||||
|
using HKDF to generate a key-encryption-key. The file-encryption key
|
||||||
|
is AEAD encrypted with this key-encryption-key. Thus, each recipient
|
||||||
|
has their own individual encrypted key blob.
|
||||||
|
|
||||||
The input to the HKDF is the root-key, header-checksum ("salt") and
|
The Ed25519 keys generated by `sigtool` are transformed to their
|
||||||
a context string.
|
corresponding Curve25519 points in order to generate the shared secret.
|
||||||
|
|
||||||
The input is broken into chunks and each chunk is individually AEAD encrypted.
|
|
||||||
The default chunk size is 4MB (4 * 1048576 bytes). Each chunk generates
|
|
||||||
its own nonce: the top-4 bytes of the nonce is the chunk-number. The
|
|
||||||
actual chunk-length and EOF marker is used as additional data (the
|
|
||||||
"AD" of "AEAD").
|
|
||||||
The last block has its most-signficant-bit set to 1 to denote EOF. Thus, the
|
|
||||||
maximum chunk size is set to 1GB.
|
|
||||||
|
|
||||||
We calculate a running hmac of the plaintext blocks; when sender
|
|
||||||
identity is present, the final HMAC is signed via the sender's
|
|
||||||
Ed25519 key. This signature is appended as the "trailer" (last 64
|
|
||||||
bytes of the encrypted file are the Ed25519 signature).
|
|
||||||
|
|
||||||
When sender identity is not present, the last bytes are random
|
|
||||||
bytes.
|
|
||||||
|
|
||||||
### What is the public-key cryptography in sigtool?
|
|
||||||
`sigtool` uses ephemeral Curve25519 keys to generate shared secrets
|
|
||||||
between pairs of sender & one or more recipients. This pairwise shared
|
|
||||||
secret is used as a key-encryption-key (KEK) to wrap the
|
|
||||||
data-encryption key in AEAD mode. Thus, each recipient has their own
|
|
||||||
individual encrypted key blob - that **only** they can decrypt.
|
|
||||||
|
|
||||||
If the sender authenticates the encryption by providing their secret
|
|
||||||
key, the encryption key material is signed via Ed25519 and the signature
|
|
||||||
is encrypted (using the data-encryption key) and stored in the
|
|
||||||
header. If the sender opts to not authenticate, a "signature" of all
|
|
||||||
zeroes is encrypted instead.
|
|
||||||
|
|
||||||
The Ed25519 keys generated by `sigtool` or OpenSSH are transformed to their
|
|
||||||
corresponding Curve25519 points in order to generate the pair-wise shared secret.
|
|
||||||
This elliptic co-ordinate transform follows [FiloSottile's writeup][2].
|
This elliptic co-ordinate transform follows [FiloSottile's writeup][2].
|
||||||
|
|
||||||
### Format of the Encrypted File
|
### Format of the Encrypted File
|
||||||
Every encrypted file starts with a header and the header-checksum:
|
Every encrypted file starts with a header:
|
||||||
|
|
||||||
* Fixed-size header
|
|
||||||
* Variable-length header
|
|
||||||
* SHA256 sum of both of the above
|
|
||||||
|
|
||||||
The fixed length header is:
|
|
||||||
|
|
||||||
7 byte magic ("SigTool")
|
7 byte magic ("SigTool")
|
||||||
1 byte version number
|
1 byte version number
|
||||||
4 byte header length (big endian encoding)
|
4 byte header length (big endian encoding)
|
||||||
|
32 byte SHA256 of the encryption-header
|
||||||
|
|
||||||
The variable length header has the per-recipient wrapped keys. This is
|
The encryption-header is described as a protobuf file (sign/hdr.proto):
|
||||||
described as a protobuf file (sign/hdr.proto):
|
|
||||||
|
|
||||||
```protobuf
|
```protobuf
|
||||||
message header {
|
message header {
|
||||||
uint32 chunk_size = 1;
|
uint32 chunk_size = 1;
|
||||||
bytes salt = 2;
|
bytes salt = 2;
|
||||||
bytes pk = 3; // sender's ephemeral curve PK
|
repeated wrapped_key keys = 3;
|
||||||
bytes sender = 4; // ed25519 signature of key material
|
|
||||||
repeated wrapped_key keys = 5;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* A file encryption key is wrapped by a recipient specific public
|
|
||||||
* key. WrappedKey describes such a wrapped key.
|
|
||||||
*/
|
|
||||||
message wrapped_key {
|
message wrapped_key {
|
||||||
bytes d_key = 1;
|
bytes pk_hash = 1; // hash of Ed25519 PK
|
||||||
bytes nonce = 2;
|
bytes pk = 2; // curve25519 PK
|
||||||
|
bytes nonce = 3; // AEAD nonce
|
||||||
|
bytes key = 4; // AEAD encrypted key
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The SHA256 sum covers the fixed-length and variable-length headers.
|
|
||||||
|
|
||||||
The encrypted data immediately follows the headers above. Each encrypted
|
The encrypted data immediately follows the headers above. Each encrypted
|
||||||
chunk is encoded the same way:
|
chunk is encoded the same way:
|
||||||
|
|
||||||
```C
|
```C
|
||||||
4 byte chunk length (big endian encoding)
|
4 byte chunk length (big endian encoding)
|
||||||
AEAD encrypted chunk data
|
chunk data
|
||||||
AEAD tag
|
AEAD tag
|
||||||
```
|
```
|
||||||
|
|
||||||
The chunk length does _not_ include the AEAD tag length; it is implicitly
|
The chunk data and AEAD tag are treated as an atomic unit for AEAD
|
||||||
computed. The chunk data and AEAD tag are treated as an atomic unit for AEAD
|
|
||||||
decryption.
|
decryption.
|
||||||
|
|
||||||
### How is the private key protected?
|
|
||||||
The Ed25519 private key is encrypted in AES-GCM-256 mode using a key
|
|
||||||
derived from the user's pass-phrase. The user pass phrase is expanded via
|
|
||||||
SHA256; this expanded pass phrase is fed to `scrypt()` to
|
|
||||||
generate a key-encryption-key. In pseudo code, this operation looks
|
|
||||||
like below:
|
|
||||||
|
|
||||||
passphrase = get_user_passphrase()
|
|
||||||
expanded = SHA512(passphrase)
|
|
||||||
salt = randombytes(32)
|
|
||||||
key = Scrypt(expanded, salt, N, r, p)
|
|
||||||
esk = AES256_GCM(ed25519_private_key, key)
|
|
||||||
|
|
||||||
Where, ```N```, ```r```, ```p``` are Scrypt parameters. In our
|
|
||||||
implementation:
|
|
||||||
|
|
||||||
N = 2^19 (1 << 19)
|
|
||||||
r = 8
|
|
||||||
p = 1
|
|
||||||
|
|
||||||
|
|
||||||
## Understanding the Code
|
## Understanding the Code
|
||||||
The core logic is in `src/sign`: it is a library that exposes all the
|
`src/sign` is a library to generate, verify and store Ed25519 keys
|
||||||
functionality: key generation, key parsing, signing, encryption, decryption
|
and signatures. It uses the extended library (golang.org/x/crypto)
|
||||||
etc.
|
for the underlying operations.
|
||||||
|
|
||||||
* `src/encrypt.go` contains the core encryption, decryption code
|
|
||||||
* `src/sign.go` contains the Ed25519 signing, verification code
|
|
||||||
* `src/keys.go` contains key generation, serialization, de-serialization
|
|
||||||
* `src/ssh.go` contains code to parse SSH Ed25519 key files
|
|
||||||
* `src/stream.go` contains code that provides an `io.Reader` and `io.WriteCloser` interface
|
|
||||||
for encryption and decryption.
|
|
||||||
* `tests.sh` simple round trip test using the tool; this is in addition to the tests in
|
|
||||||
`sign/`.
|
|
||||||
|
|
||||||
|
`src/crypt.go` contains the encryption & decryption code.
|
||||||
|
|
||||||
The generated keys and signatures are proper YAML files and human
|
The generated keys and signatures are proper YAML files and human
|
||||||
readable.
|
readable.
|
||||||
|
@ -296,11 +224,6 @@ Signatures on large files are calculated efficiently by reading them
|
||||||
in memory mapped mode (```mmap(2)```) and hashing the file contents
|
in memory mapped mode (```mmap(2)```) and hashing the file contents
|
||||||
using SHA-512. The Ed25519 signature is calculated on the file-hash.
|
using SHA-512. The Ed25519 signature is calculated on the file-hash.
|
||||||
|
|
||||||
### Tests
|
|
||||||
The core library in `sign/` has extensive tests to verify signing and encryption.
|
|
||||||
Additionally, a simple shell script `tests.sh` does a full roundtrip of tests
|
|
||||||
using `sigtool`.
|
|
||||||
|
|
||||||
## Example of Keys, Signature
|
## Example of Keys, Signature
|
||||||
|
|
||||||
### Ed25519 Public Key
|
### Ed25519 Public Key
|
||||||
|
@ -316,11 +239,35 @@ And, a serialized Ed25519 private key looks like so:
|
||||||
esk: t3vfqHbgUiA733KKPymFjWT8DdnBEkiMfsDHolPUdQWpvVn/F1Z4J6KYV3M5rGO9xgKxh5RAmqt+6LKgOiJAMQ==
|
esk: t3vfqHbgUiA733KKPymFjWT8DdnBEkiMfsDHolPUdQWpvVn/F1Z4J6KYV3M5rGO9xgKxh5RAmqt+6LKgOiJAMQ==
|
||||||
salt: pPHKG55UJYtJ5wU0G9hBvNQJ0DvT0a7T4Fmj4aPB84s=
|
salt: pPHKG55UJYtJ5wU0G9hBvNQJ0DvT0a7T4Fmj4aPB84s=
|
||||||
algo: scrypt-sha256
|
algo: scrypt-sha256
|
||||||
|
verify: JvjRjJMKhJhBmZngC3Pvq7x3KCLKt7gar1AAz7HB4qM=
|
||||||
Z: 131072
|
Z: 131072
|
||||||
r: 16
|
r: 16
|
||||||
p: 1
|
p: 1
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The Ed25519 private key is encrypted using Scrypt password hashing
|
||||||
|
mechanism. A user supplied passphrase to protect the private key
|
||||||
|
is first pre-hashed using SHA-512 before being used in
|
||||||
|
```scrypt()```. In pseudo code, this operation looks like below:
|
||||||
|
|
||||||
|
passphrase = get_user_passphrase()
|
||||||
|
hpass = SHA512(passphrase)
|
||||||
|
salt = randombytes(32)
|
||||||
|
xorkey = Scrypt(hpass, salt, N, r, p)
|
||||||
|
verify = SHA256(salt, xorkey)
|
||||||
|
esk = ed25519_private_key ^ xorkey
|
||||||
|
|
||||||
|
Where, ```N```, ```r```, ```p``` are Scrypt parameters. In our
|
||||||
|
implementation:
|
||||||
|
|
||||||
|
N = 131072
|
||||||
|
r = 16
|
||||||
|
p = 1
|
||||||
|
|
||||||
|
```verify``` is used during the decryption of the Ed25519 private
|
||||||
|
key - *before* actually doing the "xor" operation. This check
|
||||||
|
ensures that the supplied passphrase yields the same value as
|
||||||
|
```verify```.
|
||||||
|
|
||||||
### Ed25519 Signature
|
### Ed25519 Signature
|
||||||
A generated signature looks like below after serialization:
|
A generated signature looks like below after serialization:
|
||||||
|
|
271
build
271
build
|
@ -4,7 +4,7 @@
|
||||||
#
|
#
|
||||||
# - it tacks on a version number for use by the individual tools
|
# - it tacks on a version number for use by the individual tools
|
||||||
# - it supports git and mercurial version#
|
# - it supports git and mercurial version#
|
||||||
#
|
#
|
||||||
# NB:
|
# NB:
|
||||||
# o the attempt at decoding dirty repo state for mercurial is
|
# o the attempt at decoding dirty repo state for mercurial is
|
||||||
# borked. It doesn't know about untracked files
|
# borked. It doesn't know about untracked files
|
||||||
|
@ -13,13 +13,12 @@
|
||||||
#
|
#
|
||||||
# License: GPLv2
|
# License: GPLv2
|
||||||
#
|
#
|
||||||
Progs="src:sigtool"
|
Progs=".:sigtool"
|
||||||
|
|
||||||
# Relative path to protobuf sources
|
# Relative path to protobuf sources
|
||||||
# e.g. src/foo/a.proto
|
# e.g. src/foo/a.proto
|
||||||
Protobufs="internal/pb/hdr.proto"
|
Protobufs="sign/hdr.proto"
|
||||||
|
|
||||||
#set -x
|
|
||||||
|
|
||||||
# -- DO NOT CHANGE ANYTHING AFTER THIS --
|
# -- DO NOT CHANGE ANYTHING AFTER THIS --
|
||||||
|
|
||||||
|
@ -28,11 +27,13 @@ PWD=`pwd`
|
||||||
|
|
||||||
Static=0
|
Static=0
|
||||||
Dryrun=0
|
Dryrun=0
|
||||||
Prodver=""
|
Prodver=0.1
|
||||||
Repover=""
|
|
||||||
Verbose=0
|
Verbose=0
|
||||||
Go=`which go`
|
|
||||||
Bindir=$PWD/bin
|
hostos=$(go env GOHOSTOS) || exit 1
|
||||||
|
hostcpu=$(go env GOHOSTARCH) || exit 1
|
||||||
|
|
||||||
|
[ -f ./version ] && Prodver=$(cat ./version)
|
||||||
|
|
||||||
die() {
|
die() {
|
||||||
echo "$Z: $@" 1>&2
|
echo "$Z: $@" 1>&2
|
||||||
|
@ -50,56 +51,47 @@ case $BASH_VERSION in
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
getvcs_version() {
|
|
||||||
local rev=
|
|
||||||
local prodv=
|
|
||||||
local git=`which git`
|
|
||||||
local hg=`which hg`
|
|
||||||
|
|
||||||
if [ -n "$git" ]; then
|
# build a tool that runs on the host - if needed.
|
||||||
local xrev=$(git describe --always --dirty --long --abbrev=12) || exit 1
|
hosttool() {
|
||||||
rev="git:$xrev"
|
local tool=$1
|
||||||
prodv=$(git tag --list | sort -V | tail -1)
|
local bindir=$2
|
||||||
elif [ -n "$hg" ]; then
|
local src=$3
|
||||||
local xrev=$(hg id --id) || exit 1
|
|
||||||
local brev=${xrev%+}
|
local p=$(type -P $tool)
|
||||||
if [ "$brev" != "$xrev" ]; then
|
if [ -n "$p" ]; then
|
||||||
rev="hg:${brev}-dirty"
|
echo $p
|
||||||
else
|
return 0
|
||||||
rev="hg:${brev}"
|
|
||||||
fi
|
|
||||||
prodv=$(hg log -r "branch(stable) and tag()" -T "{tags}\n" | sort -V | tail -1)
|
|
||||||
else
|
|
||||||
warn "no git or hg found; can't get VCS info"
|
|
||||||
rev="UNKNOWN-VER"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
[ -n "$Prodver" ] && prodv=$Prodver
|
# from here - we want this dir to find all build artifacts
|
||||||
|
PATH=$PATH:$bindir
|
||||||
|
export PATH
|
||||||
|
|
||||||
echo "$rev $prodv"
|
p=$bindir/$tool
|
||||||
|
if [ -x $p ]; then
|
||||||
|
echo $p
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# build it and stash it in the hostdir
|
||||||
|
echo "Building tool $tool from $src .."
|
||||||
|
$e go get -d $src || exit 1
|
||||||
|
$e go build -o $p $src || exit 1
|
||||||
|
echo $p
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
read -r Repover Prodver <<< $(getvcs_version)
|
|
||||||
|
|
||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
declare -a progv=($Progs)
|
|
||||||
declare n=${#progv[@]}
|
|
||||||
declare pstr=
|
|
||||||
|
|
||||||
for ((i=0; i < n; i++)); do
|
|
||||||
local ent=${progv[$i]}
|
|
||||||
local dir=${ent%%:*}
|
|
||||||
local tool=${ent##*:}
|
|
||||||
pstr=$(printf "$pstr\n\t%s $Prodver $Repover (from ./%s)" $tool $dir)
|
|
||||||
done
|
|
||||||
|
|
||||||
cat <<EOF
|
cat <<EOF
|
||||||
$0 - A Go production build tool that adds git-repository information,
|
$0 - A Go production build tool that adds git-repository information,
|
||||||
product version, build-timestamp etc. It supports cross-compilation,
|
product version, build-timestamp etc. It supports cross-compilation,
|
||||||
static linking and generating protobuf output.
|
static linking and generating protobuf output.
|
||||||
|
|
||||||
|
If needed, it uses the gogo-slick protobuf compiler [github.com/gogo/protobuf].
|
||||||
|
|
||||||
Build output is in bin/\$OS-\$CPU for a given OS, CPU combination.
|
Build output is in bin/\$OS-\$CPU for a given OS, CPU combination.
|
||||||
|
|
||||||
Usage: $0
|
Usage: $0
|
||||||
|
@ -108,14 +100,12 @@ Usage: $0
|
||||||
Where OS-ARCH denotes one of the valid OS, ARCH combinations supported by 'go'.
|
Where OS-ARCH denotes one of the valid OS, ARCH combinations supported by 'go'.
|
||||||
And, PROGS is one or more go programs.
|
And, PROGS is one or more go programs.
|
||||||
|
|
||||||
With no arguments, $0 builds: $pstr
|
With no arguments, $0 builds: $Progs (source in ./src/)
|
||||||
|
|
||||||
The repository's latest tag is used as the default version of the software being
|
If ./version is present, its content are used as version number for the binary.
|
||||||
built. The current repository version is $Repover.
|
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-h, --help Show this help message and quit
|
-h, --help Show this help message and quit
|
||||||
-b D, --bindir=D Put the binaries in the directory 'D' [$Bindir]
|
|
||||||
-s, --static Build a statically linked binary [False]
|
-s, --static Build a statically linked binary [False]
|
||||||
-V N, --version=N Use 'N' as the product version string [$Prodver]
|
-V N, --version=N Use 'N' as the product version string [$Prodver]
|
||||||
-a X, --arch=X Cross compile for OS-CPU 'X' [$hostos-$hostcpu]
|
-a X, --arch=X Cross compile for OS-CPU 'X' [$hostos-$hostcpu]
|
||||||
|
@ -123,16 +113,14 @@ Options:
|
||||||
-t, --test Run "go test" on modules named on the command line [False]
|
-t, --test Run "go test" on modules named on the command line [False]
|
||||||
-v, --verbose Build verbosely (adds "-v" to go tooling) [False]
|
-v, --verbose Build verbosely (adds "-v" to go tooling) [False]
|
||||||
--vet Run "go vet" on modules named on the command line [False]
|
--vet Run "go vet" on modules named on the command line [False]
|
||||||
--mod Run "go mod ..." [False]
|
|
||||||
--go=G Use Go in 'G' [$Go]
|
|
||||||
-x Run in debug/trace mode [False]
|
-x Run in debug/trace mode [False]
|
||||||
--print-arch Print the target architecture and exit
|
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
exit 0
|
exit 0
|
||||||
}
|
}
|
||||||
|
|
||||||
host=`uname|tr '[A-Z]' '[a-z]'`
|
host=`uname|tr '[A-Z]' '[a-z]'`
|
||||||
|
export GO15VENDOREXPERIMENT=1
|
||||||
|
|
||||||
declare -A oses
|
declare -A oses
|
||||||
declare -A cpus
|
declare -A cpus
|
||||||
|
@ -165,7 +153,6 @@ done
|
||||||
Tool=
|
Tool=
|
||||||
doinit=0
|
doinit=0
|
||||||
args=
|
args=
|
||||||
Printarch=0
|
|
||||||
|
|
||||||
#set -x
|
#set -x
|
||||||
ac_prev=
|
ac_prev=
|
||||||
|
@ -193,23 +180,13 @@ do
|
||||||
--arch=*)
|
--arch=*)
|
||||||
Arch=$ac_optarg
|
Arch=$ac_optarg
|
||||||
;;
|
;;
|
||||||
|
|
||||||
-a|--arch)
|
-a|--arch)
|
||||||
ac_prev=Arch
|
ac_prev=Arch
|
||||||
;;
|
;;
|
||||||
|
|
||||||
-b|--bindir)
|
|
||||||
ac_prev=Bindir
|
|
||||||
;;
|
|
||||||
|
|
||||||
--bindir=*)
|
|
||||||
Bindir=$ac_optarg
|
|
||||||
;;
|
|
||||||
|
|
||||||
--version=*)
|
--version=*)
|
||||||
Prodver=$ac_optarg
|
Prodver=$ac_optarg
|
||||||
;;
|
;;
|
||||||
|
|
||||||
--test|-t)
|
--test|-t)
|
||||||
Tool=test
|
Tool=test
|
||||||
;;
|
;;
|
||||||
|
@ -218,14 +195,9 @@ do
|
||||||
Tool=vet
|
Tool=vet
|
||||||
;;
|
;;
|
||||||
|
|
||||||
--mod)
|
|
||||||
Tool=mod
|
|
||||||
;;
|
|
||||||
|
|
||||||
-V|--version)
|
-V|--version)
|
||||||
ac_prev=Prodver
|
ac_prev=Prodver
|
||||||
;;
|
;;
|
||||||
|
|
||||||
-v|--verbose)
|
-v|--verbose)
|
||||||
Verbose=1
|
Verbose=1
|
||||||
;;
|
;;
|
||||||
|
@ -242,14 +214,6 @@ do
|
||||||
set -x
|
set -x
|
||||||
;;
|
;;
|
||||||
|
|
||||||
--go-root=*)
|
|
||||||
GoRoot=$ac_optarg
|
|
||||||
;;
|
|
||||||
|
|
||||||
--print-arch)
|
|
||||||
Printarch=1
|
|
||||||
;;
|
|
||||||
|
|
||||||
*) # first non option terminates option processing.
|
*) # first non option terminates option processing.
|
||||||
# we gather all remaining args and bundle them up.
|
# we gather all remaining args and bundle them up.
|
||||||
args="$args $ac_option"
|
args="$args $ac_option"
|
||||||
|
@ -261,80 +225,10 @@ do
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
[ $Dryrun -gt 0 ] && e=echo
|
[ $Dryrun -gt 0 ] && e=echo
|
||||||
|
|
||||||
# let every error abort
|
# let every error abort
|
||||||
set -e
|
#set -e
|
||||||
|
|
||||||
# build a tool that runs on the host - if needed.
|
|
||||||
hosttool() {
|
|
||||||
local tool=$1
|
|
||||||
local bindir=$2
|
|
||||||
local src=$3
|
|
||||||
|
|
||||||
p=$bindir/$tool
|
|
||||||
if [ -x $p ]; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
local tmpdir=/tmp/$tool.$$
|
|
||||||
mkdir $tmpdir || die "can't make $tmpdir"
|
|
||||||
|
|
||||||
# since go1.20 - install uses env vars to decide where to put
|
|
||||||
# build artifacts. Why are all the google tooling so bloody dev
|
|
||||||
# hostile! WTF is wrong with command line args?!
|
|
||||||
export GOBIN=$bindir
|
|
||||||
|
|
||||||
# build it and stash it in the hostdir
|
|
||||||
echo "Building tool $tool from $src .."
|
|
||||||
(
|
|
||||||
cd $tmpdir
|
|
||||||
$e $Go install $src@latest || die "can't install $tool"
|
|
||||||
)
|
|
||||||
$e rm -rf $tmpdir
|
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
# protobuf gen
|
|
||||||
buildproto() {
|
|
||||||
local pbgo=protoc-gen-go
|
|
||||||
local vtgo=protoc-gen-go-vtproto
|
|
||||||
local vtgo_src=github.com/planetscale/vtprotobuf/cmd/protoc-gen-go-vtproto
|
|
||||||
local pc
|
|
||||||
local args="$*"
|
|
||||||
|
|
||||||
local pgen=$(type -p protoc)
|
|
||||||
local gogen=$(type -p $pbgo)
|
|
||||||
local vt=$Hostbindir/$vtgo
|
|
||||||
|
|
||||||
[ -z $pgen ] && die "install protoc tools"
|
|
||||||
[ -z $gogen ] && die "install protoc-gen-go"
|
|
||||||
|
|
||||||
# now install the vtproto generator
|
|
||||||
hosttool $vtgo $Hostbindir $vtgo_src
|
|
||||||
|
|
||||||
for f in $args; do
|
|
||||||
local dn=$(dirname $f)
|
|
||||||
local bn=$(basename $f .proto)
|
|
||||||
|
|
||||||
|
|
||||||
$e $pgen \
|
|
||||||
--go_out=. --plugin protoc-gen-go=$gogen \
|
|
||||||
--go-vtproto_out=. --plugin protoc-gen-go-vtproto="$vt" \
|
|
||||||
--go-vtproto_opt=features=marshal+unmarshal+size \
|
|
||||||
$f || die "can't generate protobuf output for $f .."
|
|
||||||
done
|
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# the rest has to execute in the context of main shell (not funcs)
|
|
||||||
|
|
||||||
hostos=$($Go env GOHOSTOS) || exit 1
|
|
||||||
hostcpu=$($Go env GOHOSTARCH) || exit 1
|
|
||||||
|
|
||||||
# This fragment can't be in a function - since it exports several vars
|
# This fragment can't be in a function - since it exports several vars
|
||||||
if [ -n "$Arch" ]; then
|
if [ -n "$Arch" ]; then
|
||||||
|
@ -380,32 +274,63 @@ else
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ $Printarch -gt 0 ]; then
|
|
||||||
echo "$hostos-$hostcpu"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
|
||||||
# This is where build outputs go
|
# This is where build outputs go
|
||||||
Outdir=$Bindir/$cross
|
Bindir=$PWD/bin/$cross
|
||||||
Hostbindir=$Bindir/$hostos-$hostcpu
|
Hostbindir=$PWD/bin/$hostos-$hostcpu
|
||||||
export PATH=$Hostbindir:$PATH
|
|
||||||
|
|
||||||
[ -d $Outdir ] || mkdir -p $Outdir
|
[ -d $Bindir ] || mkdir -p $Bindir
|
||||||
[ -d $Hostbindir ] || mkdir -p $Hostbindir
|
[ -d $Hostbindir ] || mkdir -p $Hostbindir
|
||||||
|
|
||||||
|
# Get git/hg version info for the build
|
||||||
|
if [ -d "./.hg" ]; then
|
||||||
|
xrev=$(hg id --id) || exit 1
|
||||||
|
brev=${xrev%+}
|
||||||
|
if [ "$brev" != "$xrev" ]; then
|
||||||
|
rev="hg:${brev}-dirty"
|
||||||
|
else
|
||||||
|
rev="hg:${brev}"
|
||||||
|
fi
|
||||||
|
elif [ -d "./.git" ]; then
|
||||||
|
xrev=$(git describe --always --dirty --long --abbrev=12) || exit 1
|
||||||
|
rev="git:$xrev"
|
||||||
|
else
|
||||||
|
rev="UNKNOWN-VER"
|
||||||
|
echo "$0: Can't find version info" 1>&2
|
||||||
|
fi
|
||||||
|
|
||||||
# Do Protobufs if needed
|
# Do Protobufs if needed
|
||||||
if [ -n "$Protobufs" ]; then
|
if [ -n "$Protobufs" ]; then
|
||||||
set +e
|
slick=$Hostbindir/protoc-gen-gogoslick
|
||||||
buildproto $Protobufs
|
slicksrc=github.com/gogo/protobuf/protoc-gen-gogoslick
|
||||||
set -e
|
pc=$(type -p protoc)
|
||||||
|
|
||||||
|
[ -z "$pc" ] && die "Please install protobuf-tools .."
|
||||||
|
|
||||||
|
slick=$(hosttool protoc-gen-gogoslick $Hostbindir $slicksrc) || exit 1
|
||||||
|
#if [ ! -f $slick ]; then
|
||||||
|
# echo "Building $slick .."
|
||||||
|
# $e go build -o $slick github.com/gogo/protobuf/protoc-gen-gogoslick || exit 1
|
||||||
|
#i
|
||||||
|
|
||||||
|
PATH=$Hostbindir:$PATH
|
||||||
|
export PATH
|
||||||
|
|
||||||
|
for f in $Protobufs; do
|
||||||
|
dn=$(dirname $f)
|
||||||
|
bn=$(basename $f .proto)
|
||||||
|
of=$dn/${bn}.pb.go
|
||||||
|
if [ $f -nt $of ]; then
|
||||||
|
echo "gogoslick: $f -> $of ..."
|
||||||
|
$e $pc --gogoslick_out=. $f || exit 1
|
||||||
|
$e gofmt -w $of
|
||||||
|
fi
|
||||||
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Get git/hg version info for the build
|
repover="main.RepoVersion=$rev"
|
||||||
repover="main.RepoVersion=$Repover"
|
|
||||||
prodver="main.ProductVersion=$Prodver"
|
prodver="main.ProductVersion=$Prodver"
|
||||||
ldflags="-ldflags \"-X $repover -X $prodver $ldflags -buildid=\""
|
date="main.Buildtime=`date -u '+%Y-%m-%dT%H:%M.%SZ'`"
|
||||||
|
ldflags="-ldflags \"-X $repover -X $prodver -X $date $ldflags\""
|
||||||
vflag=""
|
vflag=""
|
||||||
|
|
||||||
[ $Verbose -gt 0 ] && vflag="-v"
|
[ $Verbose -gt 0 ] && vflag="-v"
|
||||||
|
@ -413,17 +338,12 @@ vflag=""
|
||||||
case $Tool in
|
case $Tool in
|
||||||
test)
|
test)
|
||||||
set -- $args
|
set -- $args
|
||||||
$e $Go test $vflag "$@"
|
$e go test $vflag "$@"
|
||||||
;;
|
;;
|
||||||
|
|
||||||
vet)
|
vet)
|
||||||
set -- $args
|
set -- $args
|
||||||
$e $Go vet $vflag "$@"
|
$e go vet $vflag "$@"
|
||||||
;;
|
|
||||||
|
|
||||||
mod)
|
|
||||||
set -- $args
|
|
||||||
$e $Go mod $vflag "$@"
|
|
||||||
;;
|
;;
|
||||||
|
|
||||||
*) # Default is to build programs
|
*) # Default is to build programs
|
||||||
|
@ -434,11 +354,9 @@ case $Tool in
|
||||||
all="$@"
|
all="$@"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
[ -z "$all" ] && die "No programs specified. Try '$Z --help'"
|
echo "Building $msg $Prodver ($rev) for $cross .."
|
||||||
|
|
||||||
echo "Building $Prodver ($Repover), $cross $msg .."
|
for p in $all; do
|
||||||
|
|
||||||
for p in $all; do
|
|
||||||
if echo $p | grep -q ':' ; then
|
if echo $p | grep -q ':' ; then
|
||||||
out=${p##*:}
|
out=${p##*:}
|
||||||
dir=${p%%:*}
|
dir=${p%%:*}
|
||||||
|
@ -446,15 +364,8 @@ case $Tool in
|
||||||
out=$p
|
out=$p
|
||||||
dir=$p
|
dir=$p
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Add .exe suffix to out if needed
|
|
||||||
if [ "$GOOS" = "windows" ]; then
|
|
||||||
base=${out%%.exe}
|
|
||||||
out="${base}.exe"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo " $dir: $out .. "
|
echo " $dir: $out .. "
|
||||||
$e eval $Go build $vflag -trimpath -o $Outdir/$out $isuffix "$ldflags" ./$dir || exit 1
|
$e eval go build $vflag -o $Bindir/$out $isuffix "$ldflags" ./$dir || exit 1
|
||||||
done
|
done
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
|
@ -20,10 +20,9 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.rgst.io/homelab/sigtool/v3/sign"
|
|
||||||
"github.com/opencoff/go-fio"
|
|
||||||
"github.com/opencoff/go-utils"
|
"github.com/opencoff/go-utils"
|
||||||
flag "github.com/opencoff/pflag"
|
flag "github.com/opencoff/pflag"
|
||||||
|
"github.com/opencoff/sigtool/sign"
|
||||||
)
|
)
|
||||||
|
|
||||||
// sigtool encrypt [-i|--identity my.key] to.pub [to.pub] [ssh.pub] inputfile|- [-o output]
|
// sigtool encrypt [-i|--identity my.key] to.pub [to.pub] [ssh.pub] inputfile|- [-o output]
|
||||||
|
@ -36,28 +35,28 @@ func encrypt(args []string) {
|
||||||
|
|
||||||
var outfile string
|
var outfile string
|
||||||
var keyfile string
|
var keyfile string
|
||||||
var szstr string = "128k"
|
|
||||||
var envpw string
|
var envpw string
|
||||||
var nopw, force bool
|
var nopw bool
|
||||||
var blksize uint64
|
var sblksize string
|
||||||
|
|
||||||
fs.StringVarP(&outfile, "outfile", "o", "", "Write the output to file `F`")
|
fs.StringVarP(&outfile, "outfile", "o", "", "Write the output to file `F`")
|
||||||
fs.StringVarP(&keyfile, "sign", "s", "", "Sign using private key `S`")
|
fs.StringVarP(&keyfile, "sign", "s", "", "Sign using private key `S`")
|
||||||
fs.BoolVarP(&nopw, "no-password", "", false, "Don't ask for passphrase to decrypt the private key")
|
fs.BoolVarP(&nopw, "no-password", "", false, "Don't ask for passphrase to decrypt the private key")
|
||||||
fs.StringVarP(&envpw, "env-password", "E", "", "Use passphrase from environment variable `E`")
|
fs.StringVarP(&envpw, "env-password", "", "", "Use passphrase from environment variable `E`")
|
||||||
fs.StringVarP(&szstr, "block-size", "B", szstr, "Use `S` as the encryption block size")
|
fs.StringVarP(&sblksize, "block-size", "B", "4M", "Use `S` as the encryption block size")
|
||||||
fs.BoolVarP(&force, "overwrite", "", false, "Overwrite the output file if it exists")
|
|
||||||
|
|
||||||
err := fs.Parse(args)
|
err := fs.Parse(args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Die("%s", err)
|
die("%s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if blksize, err = utils.ParseSize(szstr); err != nil {
|
blksize, err := utils.ParseSize(sblksize)
|
||||||
Die("%s", err)
|
if err != nil {
|
||||||
|
die("%s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var pws, infile string
|
var pws, infile string
|
||||||
|
|
||||||
var sk *sign.PrivateKey
|
var sk *sign.PrivateKey
|
||||||
|
|
||||||
if len(keyfile) > 0 {
|
if len(keyfile) > 0 {
|
||||||
|
@ -70,23 +69,23 @@ func encrypt(args []string) {
|
||||||
} else {
|
} else {
|
||||||
pws, err = utils.Askpass("Enter passphrase for private key", false)
|
pws, err = utils.Askpass("Enter passphrase for private key", false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Die("%s", err)
|
die("%s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return []byte(pws), nil
|
return []byte(pws), nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Die("%s", err)
|
die("%s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
args = fs.Args()
|
args = fs.Args()
|
||||||
if len(args) < 2 {
|
if len(args) < 2 {
|
||||||
Die("Insufficient args. Try '%s --help'", os.Args[0])
|
die("Insufficient args. Try '%s --help'", os.Args[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
var infd io.Reader = os.Stdin
|
var infd io.Reader = os.Stdin
|
||||||
var outfd io.WriteCloser = os.Stdout
|
var outfd io.Writer = os.Stdout
|
||||||
var inf *os.File
|
var inf *os.File
|
||||||
|
|
||||||
if len(args) > 1 {
|
if len(args) > 1 {
|
||||||
|
@ -102,14 +101,14 @@ func encrypt(args []string) {
|
||||||
// Lets try to read the authorized files
|
// Lets try to read the authorized files
|
||||||
home, err := os.UserHomeDir()
|
home, err := os.UserHomeDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Die("can't find homedir for this user")
|
die("can't find homedir for this user")
|
||||||
}
|
}
|
||||||
|
|
||||||
authkeys := fmt.Sprintf("%s/.ssh/authorized_keys", home)
|
authkeys := fmt.Sprintf("%s/.ssh/authorized_keys", home)
|
||||||
authdata, err := ioutil.ReadFile(authkeys)
|
authdata, err := ioutil.ReadFile(authkeys)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !os.IsNotExist(err) {
|
if err != os.ErrNotExist {
|
||||||
Die("can't open %s: %s", authkeys, err)
|
die("can't open %s: %s", authkeys, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,43 +120,31 @@ func encrypt(args []string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(outfile) > 0 && outfile != "-" {
|
if len(outfile) > 0 && outfile != "-" {
|
||||||
var mode os.FileMode = 0600 // conservative output mode
|
|
||||||
|
|
||||||
if inf != nil {
|
if inf != nil {
|
||||||
var err error
|
ost, err := os.Stat(outfile)
|
||||||
var ist, ost os.FileInfo
|
if err != nil {
|
||||||
|
die("can't stat %s: %s", outfile, err)
|
||||||
if ost, err = os.Stat(outfile); err != nil {
|
|
||||||
Die("can't stat %s: %s", outfile, err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ist, err = inf.Stat(); err != nil {
|
ist, err := inf.Stat()
|
||||||
Die("can't stat %s: %s", infile, err)
|
if err != nil {
|
||||||
|
die("can't stat %s: %s", infile, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if os.SameFile(ist, ost) {
|
if os.SameFile(ist, ost) {
|
||||||
Die("won't create output file: same as input file!")
|
die("won't create output file: same as input file!")
|
||||||
}
|
}
|
||||||
mode = ist.Mode()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var opts uint32
|
outf := mustOpen(outfile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC)
|
||||||
if force {
|
defer outf.Close()
|
||||||
opts |= fio.OPT_OVERWRITE
|
|
||||||
}
|
|
||||||
sf, err := fio.NewSafeFile(outfile, opts, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, mode)
|
|
||||||
if err != nil {
|
|
||||||
Die("%s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
AtExit(sf.Abort)
|
outfd = outf
|
||||||
defer sf.Abort()
|
|
||||||
outfd = sf
|
|
||||||
}
|
}
|
||||||
|
|
||||||
en, err := sign.NewEncryptor(sk, blksize)
|
en, err := sign.NewEncryptor(sk, blksize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Die("%s", err)
|
die("%s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
errs := 0
|
errs := 0
|
||||||
|
@ -170,14 +157,14 @@ func encrypt(args []string) {
|
||||||
var ok bool
|
var ok bool
|
||||||
pk, ok = keymap[fn]
|
pk, ok = keymap[fn]
|
||||||
if !ok {
|
if !ok {
|
||||||
Warn("can't find user %s in %s", fn, authkeys)
|
warn("can't find user %s in %s", fn, authkeys)
|
||||||
errs += 1
|
errs += 1
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
pk, err = sign.ReadPublicKey(fn)
|
pk, err = sign.ReadPublicKey(fn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Warn("%s", err)
|
warn("%s", err)
|
||||||
errs += 1
|
errs += 1
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -185,33 +172,20 @@ func encrypt(args []string) {
|
||||||
|
|
||||||
err = en.AddRecipient(pk)
|
err = en.AddRecipient(pk)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Die("%s", err)
|
die("%s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if errs > 0 {
|
if errs > 0 {
|
||||||
Die("Too many errors!")
|
die("Too many errors!")
|
||||||
}
|
}
|
||||||
|
|
||||||
err = en.Encrypt(infd, outfd)
|
err = en.Encrypt(infd, outfd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Die("%s", err)
|
die("%s", err)
|
||||||
}
|
}
|
||||||
outfd.Close()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type nullWriter struct{}
|
|
||||||
|
|
||||||
func (w *nullWriter) Write(p []byte) (int, error) {
|
|
||||||
return len(p), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *nullWriter) Close() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ io.WriteCloser = &nullWriter{}
|
|
||||||
|
|
||||||
// sigtool decrypt a.key [file] [-o output]
|
// sigtool decrypt a.key [file] [-o output]
|
||||||
func decrypt(args []string) {
|
func decrypt(args []string) {
|
||||||
fs := flag.NewFlagSet("decrypt", flag.ExitOnError)
|
fs := flag.NewFlagSet("decrypt", flag.ExitOnError)
|
||||||
|
@ -222,27 +196,25 @@ func decrypt(args []string) {
|
||||||
var envpw string
|
var envpw string
|
||||||
var outfile string
|
var outfile string
|
||||||
var pubkey string
|
var pubkey string
|
||||||
var nopw, test, force bool
|
var nopw bool
|
||||||
|
|
||||||
fs.StringVarP(&outfile, "outfile", "o", "", "Write the output to file `F`")
|
fs.StringVarP(&outfile, "outfile", "o", "", "Write the output to file `F`")
|
||||||
fs.BoolVarP(&nopw, "no-password", "", false, "Don't ask for passphrase to decrypt the private key")
|
fs.BoolVarP(&nopw, "no-password", "", false, "Don't ask for passphrase to decrypt the private key")
|
||||||
fs.StringVarP(&envpw, "env-password", "E", "", "Use passphrase from environment variable `E`")
|
fs.StringVarP(&envpw, "env-password", "", "", "Use passphrase from environment variable `E`")
|
||||||
fs.StringVarP(&pubkey, "verify-sender", "v", "", "Verify that the sender matches public key in `F`")
|
fs.StringVarP(&pubkey, "verify-sender", "v", "", "Verify that the sender matches public key in `F`")
|
||||||
fs.BoolVarP(&test, "test", "t", false, "Test the encrypted file against the given key without writing to output")
|
|
||||||
fs.BoolVarP(&force, "overwrite", "", false, "Overwrite the output file if it exists")
|
|
||||||
|
|
||||||
err := fs.Parse(args)
|
err := fs.Parse(args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Die("%s", err)
|
die("%s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
args = fs.Args()
|
args = fs.Args()
|
||||||
if len(args) < 1 {
|
if len(args) < 1 {
|
||||||
Die("Insufficient args. Try '%s --help'", os.Args[0])
|
die("Insufficient args. Try '%s --help'", os.Args[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
var infd io.Reader = os.Stdin
|
var infd io.Reader = os.Stdin
|
||||||
var outfd io.WriteCloser = os.Stdout
|
var outfd io.Writer = os.Stdout
|
||||||
var inf *os.File
|
var inf *os.File
|
||||||
var infile string
|
var infile string
|
||||||
|
|
||||||
|
@ -258,13 +230,13 @@ func decrypt(args []string) {
|
||||||
} else {
|
} else {
|
||||||
pws, err = utils.Askpass("Enter passphrase for private key", false)
|
pws, err = utils.Askpass("Enter passphrase for private key", false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Die("%s", err)
|
die("%s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return []byte(pws), nil
|
return []byte(pws), nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Die("%s", err)
|
die("%s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var pk *sign.PublicKey
|
var pk *sign.PublicKey
|
||||||
|
@ -272,7 +244,7 @@ func decrypt(args []string) {
|
||||||
if len(pubkey) > 0 {
|
if len(pubkey) > 0 {
|
||||||
pk, err = sign.ReadPublicKey(pubkey)
|
pk, err = sign.ReadPublicKey(pubkey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Die("%s", err)
|
die("%s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -286,69 +258,41 @@ func decrypt(args []string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if test {
|
if len(outfile) > 0 && outfile != "-" {
|
||||||
outfd = &nullWriter{}
|
|
||||||
} else if len(outfile) > 0 && outfile != "-" {
|
|
||||||
var mode os.FileMode = 0600 // conservative mode
|
|
||||||
|
|
||||||
if inf != nil {
|
if inf != nil {
|
||||||
var ist, ost os.FileInfo
|
ost, err := os.Stat(outfile)
|
||||||
var err error
|
if err != nil {
|
||||||
|
die("can't stat %s: %s", outfile, err)
|
||||||
if ost, err = os.Stat(outfile); err != nil {
|
|
||||||
Die("can't stat %s: %s", outfile, err)
|
|
||||||
}
|
}
|
||||||
if ist, err = inf.Stat(); err != nil {
|
ist, err := inf.Stat()
|
||||||
Die("can't stat %s: %s", infile, err)
|
if err != nil {
|
||||||
|
die("can't stat %s: %s", infile, err)
|
||||||
}
|
}
|
||||||
if os.SameFile(ist, ost) {
|
if os.SameFile(ist, ost) {
|
||||||
Die("won't create output file: same as input file!")
|
die("won't create output file: same as input file!")
|
||||||
}
|
}
|
||||||
mode = ist.Mode()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var opts uint32
|
outf := mustOpen(outfile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC)
|
||||||
if force {
|
defer outf.Close()
|
||||||
opts |= fio.OPT_OVERWRITE
|
|
||||||
}
|
|
||||||
sf, err := fio.NewSafeFile(outfile, opts, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, mode)
|
|
||||||
if err != nil {
|
|
||||||
Die("%s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
AtExit(sf.Abort)
|
outfd = outf
|
||||||
defer sf.Abort()
|
|
||||||
outfd = sf
|
|
||||||
}
|
}
|
||||||
|
|
||||||
d, err := sign.NewDecryptor(infd)
|
d, err := sign.NewDecryptor(infd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Die("%s", err)
|
die("%s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = d.SetPrivateKey(sk, pk)
|
err = d.SetPrivateKey(sk, pk)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Die("%s", err)
|
die("%s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if pk == nil && d.AuthenticatedSender() {
|
err = d.Decrypt(outfd)
|
||||||
var fn string = infile
|
if err != nil {
|
||||||
if len(fn) == 0 || fn == "-" {
|
die("%s", err)
|
||||||
fn = "<stdin>"
|
|
||||||
}
|
|
||||||
Warn("%s: Missing sender Public Key; can't authenticate sender ..", fn)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = d.Decrypt(outfd); err != nil {
|
|
||||||
Die("%s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
outfd.Close()
|
|
||||||
|
|
||||||
if test {
|
|
||||||
Warn("Enc file OK")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func encryptUsage(fs *flag.FlagSet) {
|
func encryptUsage(fs *flag.FlagSet) {
|
||||||
|
@ -356,16 +300,9 @@ func encryptUsage(fs *flag.FlagSet) {
|
||||||
|
|
||||||
Usage: %s encrypt [options] to [to ...] infile|-
|
Usage: %s encrypt [options] to [to ...] infile|-
|
||||||
|
|
||||||
Where TO is the public key of the recipient; it can be one of:
|
Where TO is the public key of the recipient and INFILE is an input file.
|
||||||
|
If the input file is '-' then %s reads from STDIN. Unless '-o' is used,
|
||||||
- a file referring to an SSH or sigtool public key.
|
%s writes the encrypted output to STDOUT.
|
||||||
- string of the form 'a@b' - in which case the user's default
|
|
||||||
ssh/authorized_keys is consulted to find the comment matching
|
|
||||||
'a@b' - in which case the user's ssh authorized_keys file is consulted to
|
|
||||||
find the comment matching the string.
|
|
||||||
|
|
||||||
INFILE is an input file to be encrypted. If the input file is '-' then %s
|
|
||||||
reads from STDIN. Unless '-o' is used, %s writes the encrypted output to STDOUT.
|
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
`, Z, Z, Z, Z)
|
`, Z, Z, Z, Z)
|
||||||
|
@ -393,7 +330,7 @@ Options:
|
||||||
func mustOpen(fn string, flag int) *os.File {
|
func mustOpen(fn string, flag int) *os.File {
|
||||||
fdk, err := os.OpenFile(fn, flag, 0600)
|
fdk, err := os.OpenFile(fn, flag, 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Die("can't open file %s: %s", fn, err)
|
die("can't open file %s: %s", fn, err)
|
||||||
}
|
}
|
||||||
return fdk
|
return fdk
|
||||||
}
|
}
|
27
go.mod
27
go.mod
|
@ -1,25 +1,12 @@
|
||||||
module git.rgst.io/homelab/sigtool/v3
|
module github.com/opencoff/sigtool
|
||||||
|
|
||||||
go 1.24.0
|
go 1.13
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/dchest/bcrypt_pbkdf v0.0.0-20150205184540-83f37f9c154a
|
github.com/dchest/bcrypt_pbkdf v0.0.0-20150205184540-83f37f9c154a
|
||||||
github.com/opencoff/go-fio v0.5.14
|
github.com/gogo/protobuf v1.3.1
|
||||||
github.com/opencoff/go-mmap v0.1.5
|
github.com/opencoff/go-utils v0.4.0
|
||||||
github.com/opencoff/go-utils v1.0.2
|
github.com/opencoff/pflag v0.3.3
|
||||||
github.com/opencoff/pflag v1.0.7
|
golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc
|
||||||
github.com/planetscale/vtprotobuf v0.6.0
|
gopkg.in/yaml.v2 v2.2.4
|
||||||
golang.org/x/crypto v0.36.0
|
|
||||||
google.golang.org/protobuf v1.36.5
|
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/pkg/xattr v0.4.10 // indirect
|
|
||||||
github.com/puzpuzpuz/xsync/v3 v3.5.1 // indirect
|
|
||||||
golang.org/x/sys v0.31.0 // indirect
|
|
||||||
golang.org/x/term v0.30.0 // indirect
|
|
||||||
)
|
|
||||||
|
|
||||||
//replace github.com/opencoff/go-mmap => ../go-mmap
|
|
||||||
//replace github.com/opencoff/go-utils => ../go-utils
|
|
||||||
|
|
58
go.sum
58
go.sum
|
@ -1,39 +1,25 @@
|
||||||
github.com/dchest/bcrypt_pbkdf v0.0.0-20150205184540-83f37f9c154a h1:saTgr5tMLFnmy/yg3qDTft4rE5DY2uJ/cCxCe3q0XTU=
|
github.com/dchest/bcrypt_pbkdf v0.0.0-20150205184540-83f37f9c154a h1:saTgr5tMLFnmy/yg3qDTft4rE5DY2uJ/cCxCe3q0XTU=
|
||||||
github.com/dchest/bcrypt_pbkdf v0.0.0-20150205184540-83f37f9c154a/go.mod h1:Bw9BbhOJVNR+t0jCqx2GC6zv0TGBsShs56Y3gfSCvl0=
|
github.com/dchest/bcrypt_pbkdf v0.0.0-20150205184540-83f37f9c154a/go.mod h1:Bw9BbhOJVNR+t0jCqx2GC6zv0TGBsShs56Y3gfSCvl0=
|
||||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||||
github.com/opencoff/go-fio v0.5.14 h1:PGi4XLLO4RSuc3m5exY0G2vweov6w3UThhScehBfM8c=
|
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||||
github.com/opencoff/go-fio v0.5.14/go.mod h1:hoSySYpavRnfQUsxzUgadk31kYiNQhMDvA2MObsXKf8=
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
github.com/opencoff/go-mmap v0.1.5 h1:RKPtevC4mOW5bi9skBPPo4nFTIH4lVWAL20Tff+FjLg=
|
github.com/opencoff/go-utils v0.3.0 h1:/TQXjf50o3GSB9MItog5L8Gf4GWJ4B5+rmqjB4g2RZQ=
|
||||||
github.com/opencoff/go-mmap v0.1.5/go.mod h1:y/6Jk/tDUc00k3oSQpiJX++20Nw7xFSlc5kLkhGnRXw=
|
github.com/opencoff/go-utils v0.3.0/go.mod h1:c+7QUAiCCHcNH6OGvsZ0fviG7cgse8Y3ucg+xy7sGXM=
|
||||||
github.com/opencoff/go-utils v1.0.2 h1:BANRL8ZxgHpuo8gQBAzT3M9Im3aNFhaWW28jhc86LNs=
|
github.com/opencoff/go-utils v0.4.0 h1:pu08Om//u2+YGvLkHa2CyL6eI+/1J0bXih1Z6nuITp8=
|
||||||
github.com/opencoff/go-utils v1.0.2/go.mod h1:eZkEVQVzNfuE8uGepyhscMsqcXq7liGbBHYYwgYaoy8=
|
github.com/opencoff/go-utils v0.4.0/go.mod h1:c+7QUAiCCHcNH6OGvsZ0fviG7cgse8Y3ucg+xy7sGXM=
|
||||||
github.com/opencoff/pflag v1.0.7 h1:o5cQIuX75bDcdJ6AXl68gzpA72a3CJ2MPStaMnEuwi4=
|
github.com/opencoff/pflag v0.3.3 h1:yohZkwYGPkB34WXvUQzU5GyLhImnjfePDARUaE8me3U=
|
||||||
github.com/opencoff/pflag v1.0.7/go.mod h1:2bXtpAD/5h/2LarkbsRwiUxqnvB1nZBzn9Xjad1P41A=
|
github.com/opencoff/pflag v0.3.3/go.mod h1:mTLzGGUGda1Av3d34iAJlh0JIlRxmFZtmc6qoWPspK0=
|
||||||
github.com/pkg/xattr v0.4.10 h1:Qe0mtiNFHQZ296vRgUjRCoPHPqH7VdTOrZx3g0T+pGA=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
github.com/pkg/xattr v0.4.10/go.mod h1:di8WF84zAKk8jzR1UBTEWh9AUlIZZ7M/JNt8e9B6ktU=
|
golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
github.com/planetscale/vtprotobuf v0.6.0 h1:nBeETjudeJ5ZgBHUz1fVHvbqUKnYOXNhsIEabROxmNA=
|
golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc h1:c0o/qxkaO2LF5t6fQrT4b5hzyggAkLLlCUjqfRxd8Q4=
|
||||||
github.com/planetscale/vtprotobuf v0.6.0/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
|
golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
github.com/puzpuzpuz/xsync/v3 v3.5.1 h1:GJYJZwO6IdxN/IKbneznS6yPkVC+c3zyY/j19c++5Fg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
github.com/puzpuzpuz/xsync/v3 v3.5.1/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
|
||||||
golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
|
||||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
|
||||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
|
||||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
|
||||||
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
|
|
||||||
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
|
|
||||||
golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
|
|
||||||
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
|
|
||||||
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
|
|
@ -1,258 +0,0 @@
|
||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
|
||||||
// versions:
|
|
||||||
// protoc-gen-go v1.32.0
|
|
||||||
// protoc v3.21.12
|
|
||||||
// source: internal/pb/hdr.proto
|
|
||||||
|
|
||||||
package pb
|
|
||||||
|
|
||||||
import (
|
|
||||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
|
||||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
|
||||||
reflect "reflect"
|
|
||||||
sync "sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// Verify that this generated code is sufficiently up-to-date.
|
|
||||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
|
||||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
|
||||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
|
||||||
)
|
|
||||||
|
|
||||||
// Every encrypted file starts with a header describing the
|
|
||||||
// Block Size, Salt, Recipient keys etc. Header represents a
|
|
||||||
// decoded version of this information. It is encoded in
|
|
||||||
// protobuf format before writing to disk.
|
|
||||||
type Header struct {
|
|
||||||
state protoimpl.MessageState
|
|
||||||
sizeCache protoimpl.SizeCache
|
|
||||||
unknownFields protoimpl.UnknownFields
|
|
||||||
|
|
||||||
ChunkSize uint32 `protobuf:"varint,1,opt,name=chunk_size,json=chunkSize,proto3" json:"chunk_size,omitempty"` // encryption block size
|
|
||||||
Salt []byte `protobuf:"bytes,2,opt,name=salt,proto3" json:"salt,omitempty"` // master salt (nonces are derived from this)
|
|
||||||
Pk []byte `protobuf:"bytes,3,opt,name=pk,proto3" json:"pk,omitempty"` // ephemeral curve PK
|
|
||||||
Sender []byte `protobuf:"bytes,4,opt,name=sender,proto3" json:"sender,omitempty"` // sender signed artifacts
|
|
||||||
Keys []*WrappedKey `protobuf:"bytes,5,rep,name=keys,proto3" json:"keys,omitempty"` // list of wrapped receiver blocks
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Header) Reset() {
|
|
||||||
*x = Header{}
|
|
||||||
if protoimpl.UnsafeEnabled {
|
|
||||||
mi := &file_internal_pb_hdr_proto_msgTypes[0]
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Header) String() string {
|
|
||||||
return protoimpl.X.MessageStringOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*Header) ProtoMessage() {}
|
|
||||||
|
|
||||||
func (x *Header) ProtoReflect() protoreflect.Message {
|
|
||||||
mi := &file_internal_pb_hdr_proto_msgTypes[0]
|
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
if ms.LoadMessageInfo() == nil {
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
return ms
|
|
||||||
}
|
|
||||||
return mi.MessageOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Use Header.ProtoReflect.Descriptor instead.
|
|
||||||
func (*Header) Descriptor() ([]byte, []int) {
|
|
||||||
return file_internal_pb_hdr_proto_rawDescGZIP(), []int{0}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Header) GetChunkSize() uint32 {
|
|
||||||
if x != nil {
|
|
||||||
return x.ChunkSize
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Header) GetSalt() []byte {
|
|
||||||
if x != nil {
|
|
||||||
return x.Salt
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Header) GetPk() []byte {
|
|
||||||
if x != nil {
|
|
||||||
return x.Pk
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Header) GetSender() []byte {
|
|
||||||
if x != nil {
|
|
||||||
return x.Sender
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Header) GetKeys() []*WrappedKey {
|
|
||||||
if x != nil {
|
|
||||||
return x.Keys
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// A file encryption key is wrapped by a recipient specific public
|
|
||||||
// key. WrappedKey describes such a wrapped key.
|
|
||||||
type WrappedKey struct {
|
|
||||||
state protoimpl.MessageState
|
|
||||||
sizeCache protoimpl.SizeCache
|
|
||||||
unknownFields protoimpl.UnknownFields
|
|
||||||
|
|
||||||
DKey []byte `protobuf:"bytes,1,opt,name=d_key,json=dKey,proto3" json:"d_key,omitempty"` // encrypted data key
|
|
||||||
Nonce []byte `protobuf:"bytes,2,opt,name=nonce,proto3" json:"nonce,omitempty"` // nonce used for encryption
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *WrappedKey) Reset() {
|
|
||||||
*x = WrappedKey{}
|
|
||||||
if protoimpl.UnsafeEnabled {
|
|
||||||
mi := &file_internal_pb_hdr_proto_msgTypes[1]
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *WrappedKey) String() string {
|
|
||||||
return protoimpl.X.MessageStringOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*WrappedKey) ProtoMessage() {}
|
|
||||||
|
|
||||||
func (x *WrappedKey) ProtoReflect() protoreflect.Message {
|
|
||||||
mi := &file_internal_pb_hdr_proto_msgTypes[1]
|
|
||||||
if protoimpl.UnsafeEnabled && x != nil {
|
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
|
||||||
if ms.LoadMessageInfo() == nil {
|
|
||||||
ms.StoreMessageInfo(mi)
|
|
||||||
}
|
|
||||||
return ms
|
|
||||||
}
|
|
||||||
return mi.MessageOf(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deprecated: Use WrappedKey.ProtoReflect.Descriptor instead.
|
|
||||||
func (*WrappedKey) Descriptor() ([]byte, []int) {
|
|
||||||
return file_internal_pb_hdr_proto_rawDescGZIP(), []int{1}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *WrappedKey) GetDKey() []byte {
|
|
||||||
if x != nil {
|
|
||||||
return x.DKey
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *WrappedKey) GetNonce() []byte {
|
|
||||||
if x != nil {
|
|
||||||
return x.Nonce
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var File_internal_pb_hdr_proto protoreflect.FileDescriptor
|
|
||||||
|
|
||||||
var file_internal_pb_hdr_proto_rawDesc = []byte{
|
|
||||||
0x0a, 0x15, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x62, 0x2f, 0x68, 0x64,
|
|
||||||
0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x85, 0x01, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64,
|
|
||||||
0x65, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x5f, 0x73, 0x69, 0x7a, 0x65,
|
|
||||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x63, 0x68, 0x75, 0x6e, 0x6b, 0x53, 0x69, 0x7a,
|
|
||||||
0x65, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x61, 0x6c, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52,
|
|
||||||
0x04, 0x73, 0x61, 0x6c, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x70, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28,
|
|
||||||
0x0c, 0x52, 0x02, 0x70, 0x6b, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x18,
|
|
||||||
0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x12, 0x20, 0x0a,
|
|
||||||
0x04, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x77, 0x72,
|
|
||||||
0x61, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x52, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x22,
|
|
||||||
0x38, 0x0a, 0x0b, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x12, 0x13,
|
|
||||||
0x0a, 0x05, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64,
|
|
||||||
0x4b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01,
|
|
||||||
0x28, 0x0c, 0x52, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x42, 0x0d, 0x5a, 0x0b, 0x69, 0x6e, 0x74,
|
|
||||||
0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
file_internal_pb_hdr_proto_rawDescOnce sync.Once
|
|
||||||
file_internal_pb_hdr_proto_rawDescData = file_internal_pb_hdr_proto_rawDesc
|
|
||||||
)
|
|
||||||
|
|
||||||
func file_internal_pb_hdr_proto_rawDescGZIP() []byte {
|
|
||||||
file_internal_pb_hdr_proto_rawDescOnce.Do(func() {
|
|
||||||
file_internal_pb_hdr_proto_rawDescData = protoimpl.X.CompressGZIP(file_internal_pb_hdr_proto_rawDescData)
|
|
||||||
})
|
|
||||||
return file_internal_pb_hdr_proto_rawDescData
|
|
||||||
}
|
|
||||||
|
|
||||||
var file_internal_pb_hdr_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
|
|
||||||
var file_internal_pb_hdr_proto_goTypes = []interface{}{
|
|
||||||
(*Header)(nil), // 0: header
|
|
||||||
(*WrappedKey)(nil), // 1: wrapped_key
|
|
||||||
}
|
|
||||||
var file_internal_pb_hdr_proto_depIdxs = []int32{
|
|
||||||
1, // 0: header.keys:type_name -> wrapped_key
|
|
||||||
1, // [1:1] is the sub-list for method output_type
|
|
||||||
1, // [1:1] is the sub-list for method input_type
|
|
||||||
1, // [1:1] is the sub-list for extension type_name
|
|
||||||
1, // [1:1] is the sub-list for extension extendee
|
|
||||||
0, // [0:1] is the sub-list for field type_name
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() { file_internal_pb_hdr_proto_init() }
|
|
||||||
func file_internal_pb_hdr_proto_init() {
|
|
||||||
if File_internal_pb_hdr_proto != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !protoimpl.UnsafeEnabled {
|
|
||||||
file_internal_pb_hdr_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
|
||||||
switch v := v.(*Header); i {
|
|
||||||
case 0:
|
|
||||||
return &v.state
|
|
||||||
case 1:
|
|
||||||
return &v.sizeCache
|
|
||||||
case 2:
|
|
||||||
return &v.unknownFields
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
file_internal_pb_hdr_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
|
||||||
switch v := v.(*WrappedKey); i {
|
|
||||||
case 0:
|
|
||||||
return &v.state
|
|
||||||
case 1:
|
|
||||||
return &v.sizeCache
|
|
||||||
case 2:
|
|
||||||
return &v.unknownFields
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
type x struct{}
|
|
||||||
out := protoimpl.TypeBuilder{
|
|
||||||
File: protoimpl.DescBuilder{
|
|
||||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
|
||||||
RawDescriptor: file_internal_pb_hdr_proto_rawDesc,
|
|
||||||
NumEnums: 0,
|
|
||||||
NumMessages: 2,
|
|
||||||
NumExtensions: 0,
|
|
||||||
NumServices: 0,
|
|
||||||
},
|
|
||||||
GoTypes: file_internal_pb_hdr_proto_goTypes,
|
|
||||||
DependencyIndexes: file_internal_pb_hdr_proto_depIdxs,
|
|
||||||
MessageInfos: file_internal_pb_hdr_proto_msgTypes,
|
|
||||||
}.Build()
|
|
||||||
File_internal_pb_hdr_proto = out.File
|
|
||||||
file_internal_pb_hdr_proto_rawDesc = nil
|
|
||||||
file_internal_pb_hdr_proto_goTypes = nil
|
|
||||||
file_internal_pb_hdr_proto_depIdxs = nil
|
|
||||||
}
|
|
|
@ -1,29 +0,0 @@
|
||||||
syntax="proto3";
|
|
||||||
|
|
||||||
|
|
||||||
option go_package = "internal/pb";
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Every encrypted file starts with a header describing the
|
|
||||||
* Block Size, Salt, Recipient keys etc. Header represents a
|
|
||||||
* decoded version of this information. It is encoded in
|
|
||||||
* protobuf format before writing to disk.
|
|
||||||
*/
|
|
||||||
message header {
|
|
||||||
uint32 chunk_size = 1; // encryption block size
|
|
||||||
bytes salt = 2; // master salt (nonces are derived from this)
|
|
||||||
bytes pk = 3; // ephemeral curve PK
|
|
||||||
bytes sender = 4; // sender signed artifacts
|
|
||||||
repeated wrapped_key keys = 5; // list of wrapped receiver blocks
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* A file encryption key is wrapped by a recipient specific public
|
|
||||||
* key. WrappedKey describes such a wrapped key.
|
|
||||||
*/
|
|
||||||
message wrapped_key {
|
|
||||||
bytes d_key = 1; // encrypted data key
|
|
||||||
bytes nonce = 2; // nonce used for encryption
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,512 +0,0 @@
|
||||||
// Code generated by protoc-gen-go-vtproto. DO NOT EDIT.
|
|
||||||
// protoc-gen-go-vtproto version: v0.6.0
|
|
||||||
// source: internal/pb/hdr.proto
|
|
||||||
|
|
||||||
package pb
|
|
||||||
|
|
||||||
import (
|
|
||||||
fmt "fmt"
|
|
||||||
protohelpers "github.com/planetscale/vtprotobuf/protohelpers"
|
|
||||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
|
||||||
io "io"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// Verify that this generated code is sufficiently up-to-date.
|
|
||||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
|
||||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
|
||||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
|
||||||
)
|
|
||||||
|
|
||||||
func (m *Header) MarshalVT() (dAtA []byte, err error) {
|
|
||||||
if m == nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
size := m.SizeVT()
|
|
||||||
dAtA = make([]byte, size)
|
|
||||||
n, err := m.MarshalToSizedBufferVT(dAtA[:size])
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return dAtA[:n], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Header) MarshalToVT(dAtA []byte) (int, error) {
|
|
||||||
size := m.SizeVT()
|
|
||||||
return m.MarshalToSizedBufferVT(dAtA[:size])
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Header) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
|
|
||||||
if m == nil {
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
i := len(dAtA)
|
|
||||||
_ = i
|
|
||||||
var l int
|
|
||||||
_ = l
|
|
||||||
if m.unknownFields != nil {
|
|
||||||
i -= len(m.unknownFields)
|
|
||||||
copy(dAtA[i:], m.unknownFields)
|
|
||||||
}
|
|
||||||
if len(m.Keys) > 0 {
|
|
||||||
for iNdEx := len(m.Keys) - 1; iNdEx >= 0; iNdEx-- {
|
|
||||||
size, err := m.Keys[iNdEx].MarshalToSizedBufferVT(dAtA[:i])
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
i -= size
|
|
||||||
i = protohelpers.EncodeVarint(dAtA, i, uint64(size))
|
|
||||||
i--
|
|
||||||
dAtA[i] = 0x2a
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(m.Sender) > 0 {
|
|
||||||
i -= len(m.Sender)
|
|
||||||
copy(dAtA[i:], m.Sender)
|
|
||||||
i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Sender)))
|
|
||||||
i--
|
|
||||||
dAtA[i] = 0x22
|
|
||||||
}
|
|
||||||
if len(m.Pk) > 0 {
|
|
||||||
i -= len(m.Pk)
|
|
||||||
copy(dAtA[i:], m.Pk)
|
|
||||||
i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Pk)))
|
|
||||||
i--
|
|
||||||
dAtA[i] = 0x1a
|
|
||||||
}
|
|
||||||
if len(m.Salt) > 0 {
|
|
||||||
i -= len(m.Salt)
|
|
||||||
copy(dAtA[i:], m.Salt)
|
|
||||||
i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Salt)))
|
|
||||||
i--
|
|
||||||
dAtA[i] = 0x12
|
|
||||||
}
|
|
||||||
if m.ChunkSize != 0 {
|
|
||||||
i = protohelpers.EncodeVarint(dAtA, i, uint64(m.ChunkSize))
|
|
||||||
i--
|
|
||||||
dAtA[i] = 0x8
|
|
||||||
}
|
|
||||||
return len(dAtA) - i, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *WrappedKey) MarshalVT() (dAtA []byte, err error) {
|
|
||||||
if m == nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
size := m.SizeVT()
|
|
||||||
dAtA = make([]byte, size)
|
|
||||||
n, err := m.MarshalToSizedBufferVT(dAtA[:size])
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return dAtA[:n], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *WrappedKey) MarshalToVT(dAtA []byte) (int, error) {
|
|
||||||
size := m.SizeVT()
|
|
||||||
return m.MarshalToSizedBufferVT(dAtA[:size])
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *WrappedKey) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
|
|
||||||
if m == nil {
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
i := len(dAtA)
|
|
||||||
_ = i
|
|
||||||
var l int
|
|
||||||
_ = l
|
|
||||||
if m.unknownFields != nil {
|
|
||||||
i -= len(m.unknownFields)
|
|
||||||
copy(dAtA[i:], m.unknownFields)
|
|
||||||
}
|
|
||||||
if len(m.Nonce) > 0 {
|
|
||||||
i -= len(m.Nonce)
|
|
||||||
copy(dAtA[i:], m.Nonce)
|
|
||||||
i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Nonce)))
|
|
||||||
i--
|
|
||||||
dAtA[i] = 0x12
|
|
||||||
}
|
|
||||||
if len(m.DKey) > 0 {
|
|
||||||
i -= len(m.DKey)
|
|
||||||
copy(dAtA[i:], m.DKey)
|
|
||||||
i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.DKey)))
|
|
||||||
i--
|
|
||||||
dAtA[i] = 0xa
|
|
||||||
}
|
|
||||||
return len(dAtA) - i, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Header) SizeVT() (n int) {
|
|
||||||
if m == nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
var l int
|
|
||||||
_ = l
|
|
||||||
if m.ChunkSize != 0 {
|
|
||||||
n += 1 + protohelpers.SizeOfVarint(uint64(m.ChunkSize))
|
|
||||||
}
|
|
||||||
l = len(m.Salt)
|
|
||||||
if l > 0 {
|
|
||||||
n += 1 + l + protohelpers.SizeOfVarint(uint64(l))
|
|
||||||
}
|
|
||||||
l = len(m.Pk)
|
|
||||||
if l > 0 {
|
|
||||||
n += 1 + l + protohelpers.SizeOfVarint(uint64(l))
|
|
||||||
}
|
|
||||||
l = len(m.Sender)
|
|
||||||
if l > 0 {
|
|
||||||
n += 1 + l + protohelpers.SizeOfVarint(uint64(l))
|
|
||||||
}
|
|
||||||
if len(m.Keys) > 0 {
|
|
||||||
for _, e := range m.Keys {
|
|
||||||
l = e.SizeVT()
|
|
||||||
n += 1 + l + protohelpers.SizeOfVarint(uint64(l))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
n += len(m.unknownFields)
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *WrappedKey) SizeVT() (n int) {
|
|
||||||
if m == nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
var l int
|
|
||||||
_ = l
|
|
||||||
l = len(m.DKey)
|
|
||||||
if l > 0 {
|
|
||||||
n += 1 + l + protohelpers.SizeOfVarint(uint64(l))
|
|
||||||
}
|
|
||||||
l = len(m.Nonce)
|
|
||||||
if l > 0 {
|
|
||||||
n += 1 + l + protohelpers.SizeOfVarint(uint64(l))
|
|
||||||
}
|
|
||||||
n += len(m.unknownFields)
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Header) UnmarshalVT(dAtA []byte) error {
|
|
||||||
l := len(dAtA)
|
|
||||||
iNdEx := 0
|
|
||||||
for iNdEx < l {
|
|
||||||
preIndex := iNdEx
|
|
||||||
var wire uint64
|
|
||||||
for shift := uint(0); ; shift += 7 {
|
|
||||||
if shift >= 64 {
|
|
||||||
return protohelpers.ErrIntOverflow
|
|
||||||
}
|
|
||||||
if iNdEx >= l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
b := dAtA[iNdEx]
|
|
||||||
iNdEx++
|
|
||||||
wire |= uint64(b&0x7F) << shift
|
|
||||||
if b < 0x80 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fieldNum := int32(wire >> 3)
|
|
||||||
wireType := int(wire & 0x7)
|
|
||||||
if wireType == 4 {
|
|
||||||
return fmt.Errorf("proto: Header: wiretype end group for non-group")
|
|
||||||
}
|
|
||||||
if fieldNum <= 0 {
|
|
||||||
return fmt.Errorf("proto: Header: illegal tag %d (wire type %d)", fieldNum, wire)
|
|
||||||
}
|
|
||||||
switch fieldNum {
|
|
||||||
case 1:
|
|
||||||
if wireType != 0 {
|
|
||||||
return fmt.Errorf("proto: wrong wireType = %d for field ChunkSize", wireType)
|
|
||||||
}
|
|
||||||
m.ChunkSize = 0
|
|
||||||
for shift := uint(0); ; shift += 7 {
|
|
||||||
if shift >= 64 {
|
|
||||||
return protohelpers.ErrIntOverflow
|
|
||||||
}
|
|
||||||
if iNdEx >= l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
b := dAtA[iNdEx]
|
|
||||||
iNdEx++
|
|
||||||
m.ChunkSize |= uint32(b&0x7F) << shift
|
|
||||||
if b < 0x80 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case 2:
|
|
||||||
if wireType != 2 {
|
|
||||||
return fmt.Errorf("proto: wrong wireType = %d for field Salt", wireType)
|
|
||||||
}
|
|
||||||
var byteLen int
|
|
||||||
for shift := uint(0); ; shift += 7 {
|
|
||||||
if shift >= 64 {
|
|
||||||
return protohelpers.ErrIntOverflow
|
|
||||||
}
|
|
||||||
if iNdEx >= l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
b := dAtA[iNdEx]
|
|
||||||
iNdEx++
|
|
||||||
byteLen |= int(b&0x7F) << shift
|
|
||||||
if b < 0x80 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if byteLen < 0 {
|
|
||||||
return protohelpers.ErrInvalidLength
|
|
||||||
}
|
|
||||||
postIndex := iNdEx + byteLen
|
|
||||||
if postIndex < 0 {
|
|
||||||
return protohelpers.ErrInvalidLength
|
|
||||||
}
|
|
||||||
if postIndex > l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
m.Salt = append(m.Salt[:0], dAtA[iNdEx:postIndex]...)
|
|
||||||
if m.Salt == nil {
|
|
||||||
m.Salt = []byte{}
|
|
||||||
}
|
|
||||||
iNdEx = postIndex
|
|
||||||
case 3:
|
|
||||||
if wireType != 2 {
|
|
||||||
return fmt.Errorf("proto: wrong wireType = %d for field Pk", wireType)
|
|
||||||
}
|
|
||||||
var byteLen int
|
|
||||||
for shift := uint(0); ; shift += 7 {
|
|
||||||
if shift >= 64 {
|
|
||||||
return protohelpers.ErrIntOverflow
|
|
||||||
}
|
|
||||||
if iNdEx >= l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
b := dAtA[iNdEx]
|
|
||||||
iNdEx++
|
|
||||||
byteLen |= int(b&0x7F) << shift
|
|
||||||
if b < 0x80 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if byteLen < 0 {
|
|
||||||
return protohelpers.ErrInvalidLength
|
|
||||||
}
|
|
||||||
postIndex := iNdEx + byteLen
|
|
||||||
if postIndex < 0 {
|
|
||||||
return protohelpers.ErrInvalidLength
|
|
||||||
}
|
|
||||||
if postIndex > l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
m.Pk = append(m.Pk[:0], dAtA[iNdEx:postIndex]...)
|
|
||||||
if m.Pk == nil {
|
|
||||||
m.Pk = []byte{}
|
|
||||||
}
|
|
||||||
iNdEx = postIndex
|
|
||||||
case 4:
|
|
||||||
if wireType != 2 {
|
|
||||||
return fmt.Errorf("proto: wrong wireType = %d for field Sender", wireType)
|
|
||||||
}
|
|
||||||
var byteLen int
|
|
||||||
for shift := uint(0); ; shift += 7 {
|
|
||||||
if shift >= 64 {
|
|
||||||
return protohelpers.ErrIntOverflow
|
|
||||||
}
|
|
||||||
if iNdEx >= l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
b := dAtA[iNdEx]
|
|
||||||
iNdEx++
|
|
||||||
byteLen |= int(b&0x7F) << shift
|
|
||||||
if b < 0x80 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if byteLen < 0 {
|
|
||||||
return protohelpers.ErrInvalidLength
|
|
||||||
}
|
|
||||||
postIndex := iNdEx + byteLen
|
|
||||||
if postIndex < 0 {
|
|
||||||
return protohelpers.ErrInvalidLength
|
|
||||||
}
|
|
||||||
if postIndex > l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
m.Sender = append(m.Sender[:0], dAtA[iNdEx:postIndex]...)
|
|
||||||
if m.Sender == nil {
|
|
||||||
m.Sender = []byte{}
|
|
||||||
}
|
|
||||||
iNdEx = postIndex
|
|
||||||
case 5:
|
|
||||||
if wireType != 2 {
|
|
||||||
return fmt.Errorf("proto: wrong wireType = %d for field Keys", wireType)
|
|
||||||
}
|
|
||||||
var msglen int
|
|
||||||
for shift := uint(0); ; shift += 7 {
|
|
||||||
if shift >= 64 {
|
|
||||||
return protohelpers.ErrIntOverflow
|
|
||||||
}
|
|
||||||
if iNdEx >= l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
b := dAtA[iNdEx]
|
|
||||||
iNdEx++
|
|
||||||
msglen |= int(b&0x7F) << shift
|
|
||||||
if b < 0x80 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if msglen < 0 {
|
|
||||||
return protohelpers.ErrInvalidLength
|
|
||||||
}
|
|
||||||
postIndex := iNdEx + msglen
|
|
||||||
if postIndex < 0 {
|
|
||||||
return protohelpers.ErrInvalidLength
|
|
||||||
}
|
|
||||||
if postIndex > l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
m.Keys = append(m.Keys, &WrappedKey{})
|
|
||||||
if err := m.Keys[len(m.Keys)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
iNdEx = postIndex
|
|
||||||
default:
|
|
||||||
iNdEx = preIndex
|
|
||||||
skippy, err := protohelpers.Skip(dAtA[iNdEx:])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
|
||||||
return protohelpers.ErrInvalidLength
|
|
||||||
}
|
|
||||||
if (iNdEx + skippy) > l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...)
|
|
||||||
iNdEx += skippy
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if iNdEx > l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func (m *WrappedKey) UnmarshalVT(dAtA []byte) error {
|
|
||||||
l := len(dAtA)
|
|
||||||
iNdEx := 0
|
|
||||||
for iNdEx < l {
|
|
||||||
preIndex := iNdEx
|
|
||||||
var wire uint64
|
|
||||||
for shift := uint(0); ; shift += 7 {
|
|
||||||
if shift >= 64 {
|
|
||||||
return protohelpers.ErrIntOverflow
|
|
||||||
}
|
|
||||||
if iNdEx >= l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
b := dAtA[iNdEx]
|
|
||||||
iNdEx++
|
|
||||||
wire |= uint64(b&0x7F) << shift
|
|
||||||
if b < 0x80 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fieldNum := int32(wire >> 3)
|
|
||||||
wireType := int(wire & 0x7)
|
|
||||||
if wireType == 4 {
|
|
||||||
return fmt.Errorf("proto: WrappedKey: wiretype end group for non-group")
|
|
||||||
}
|
|
||||||
if fieldNum <= 0 {
|
|
||||||
return fmt.Errorf("proto: WrappedKey: illegal tag %d (wire type %d)", fieldNum, wire)
|
|
||||||
}
|
|
||||||
switch fieldNum {
|
|
||||||
case 1:
|
|
||||||
if wireType != 2 {
|
|
||||||
return fmt.Errorf("proto: wrong wireType = %d for field DKey", wireType)
|
|
||||||
}
|
|
||||||
var byteLen int
|
|
||||||
for shift := uint(0); ; shift += 7 {
|
|
||||||
if shift >= 64 {
|
|
||||||
return protohelpers.ErrIntOverflow
|
|
||||||
}
|
|
||||||
if iNdEx >= l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
b := dAtA[iNdEx]
|
|
||||||
iNdEx++
|
|
||||||
byteLen |= int(b&0x7F) << shift
|
|
||||||
if b < 0x80 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if byteLen < 0 {
|
|
||||||
return protohelpers.ErrInvalidLength
|
|
||||||
}
|
|
||||||
postIndex := iNdEx + byteLen
|
|
||||||
if postIndex < 0 {
|
|
||||||
return protohelpers.ErrInvalidLength
|
|
||||||
}
|
|
||||||
if postIndex > l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
m.DKey = append(m.DKey[:0], dAtA[iNdEx:postIndex]...)
|
|
||||||
if m.DKey == nil {
|
|
||||||
m.DKey = []byte{}
|
|
||||||
}
|
|
||||||
iNdEx = postIndex
|
|
||||||
case 2:
|
|
||||||
if wireType != 2 {
|
|
||||||
return fmt.Errorf("proto: wrong wireType = %d for field Nonce", wireType)
|
|
||||||
}
|
|
||||||
var byteLen int
|
|
||||||
for shift := uint(0); ; shift += 7 {
|
|
||||||
if shift >= 64 {
|
|
||||||
return protohelpers.ErrIntOverflow
|
|
||||||
}
|
|
||||||
if iNdEx >= l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
b := dAtA[iNdEx]
|
|
||||||
iNdEx++
|
|
||||||
byteLen |= int(b&0x7F) << shift
|
|
||||||
if b < 0x80 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if byteLen < 0 {
|
|
||||||
return protohelpers.ErrInvalidLength
|
|
||||||
}
|
|
||||||
postIndex := iNdEx + byteLen
|
|
||||||
if postIndex < 0 {
|
|
||||||
return protohelpers.ErrInvalidLength
|
|
||||||
}
|
|
||||||
if postIndex > l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
m.Nonce = append(m.Nonce[:0], dAtA[iNdEx:postIndex]...)
|
|
||||||
if m.Nonce == nil {
|
|
||||||
m.Nonce = []byte{}
|
|
||||||
}
|
|
||||||
iNdEx = postIndex
|
|
||||||
default:
|
|
||||||
iNdEx = preIndex
|
|
||||||
skippy, err := protohelpers.Skip(dAtA[iNdEx:])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
|
||||||
return protohelpers.ErrInvalidLength
|
|
||||||
}
|
|
||||||
if (iNdEx + skippy) > l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...)
|
|
||||||
iNdEx += skippy
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if iNdEx > l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
// helper routines to maintain backwards compat with gogo
|
|
||||||
|
|
||||||
package pb
|
|
||||||
|
|
||||||
|
|
||||||
func (m *Header) Size() int {
|
|
||||||
return m.SizeVT()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Header) MarshalTo(buf []byte) (int, error) {
|
|
||||||
return m.MarshalToVT(buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Header) Unmarshal(buf []byte) error {
|
|
||||||
return m.UnmarshalVT(buf)
|
|
||||||
}
|
|
54
mk-rel.sh
54
mk-rel.sh
|
@ -1,54 +0,0 @@
|
||||||
#! /usr/bin/env bash
|
|
||||||
|
|
||||||
Z=`basename $0`
|
|
||||||
die() {
|
|
||||||
echo "$Z: $@" 1>&2
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
warn() {
|
|
||||||
echo "$Z: $@" 1>&2
|
|
||||||
}
|
|
||||||
|
|
||||||
case $BASH_VERSION in
|
|
||||||
4.*|5.*) ;;
|
|
||||||
|
|
||||||
*) die "I need bash 4.x to run!"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
Rel=$PWD/releases
|
|
||||||
Bindir=$Rel/bin
|
|
||||||
mkdir -p $Bindir || die "can't make $Bindir"
|
|
||||||
|
|
||||||
pkgit() {
|
|
||||||
local os=$1
|
|
||||||
local cpu=$2
|
|
||||||
local rev=$3
|
|
||||||
local arch="$os-$cpu"
|
|
||||||
local tgz="$Rel/sigtool-${rev}_${arch}.tar.gz"
|
|
||||||
local bindir=$Bindir/$arch
|
|
||||||
local bin=sigtool
|
|
||||||
|
|
||||||
if [ "$os" = "windows" ]; then
|
|
||||||
bin=${bin}.exe
|
|
||||||
fi
|
|
||||||
|
|
||||||
./build -V $rev -b $Bindir -s -a $arch || die "can't build $arch"
|
|
||||||
(cd $bindir && tar cf - $bin) | gzip -9 > $tgz || die "can't tar $tgz"
|
|
||||||
}
|
|
||||||
|
|
||||||
xrev=$(git describe --always --dirty --abbrev=12) || exit 1
|
|
||||||
if echo $xrev | grep -q dirty; then
|
|
||||||
die "won't build releases; repo dirty!"
|
|
||||||
true
|
|
||||||
fi
|
|
||||||
|
|
||||||
os="linux windows openbsd darwin"
|
|
||||||
arch="amd64 arm64"
|
|
||||||
|
|
||||||
for xx in $os; do
|
|
||||||
for yy in $arch; do
|
|
||||||
pkgit $xx $yy $xrev
|
|
||||||
done
|
|
||||||
done
|
|
|
@ -1,3 +0,0 @@
|
||||||
{
|
|
||||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json"
|
|
||||||
}
|
|
|
@ -1,10 +1,10 @@
|
||||||
[](https://godoc.org/git.rgst.io/homelab/sigtool/v3/sign)
|
[](https://godoc.org/github.com/opencoff/sigtool/sign)
|
||||||
|
|
||||||
# sigtool/sign - Ed25519 signature calculation and verification
|
# sigtool/sign - Ed25519 signature calculation and verification
|
||||||
|
|
||||||
This is a small library that makes it easier to create and serialize Ed25519 keys, and sign,
|
This is a small library that makes it easier to create and serialize Ed25519 keys, and sign,
|
||||||
verify files using those keys. The library uses mmap(2) to read and process very large files.
|
verify files using those keys. The library uses mmap(2) to read and process very large files.
|
||||||
|
|
||||||
The companion program [sigtool](https://git.rgst.io/homelab/sigtool/v3) uses this library.
|
The companion program [sigtool](https://github.com/opencoff/sigtool) uses this library.
|
||||||
## License
|
## License
|
||||||
GPL v2.0
|
GPL v2.0
|
||||||
|
|
218
sign/crypt_test.go
Normal file
218
sign/crypt_test.go
Normal file
|
@ -0,0 +1,218 @@
|
||||||
|
// crypt_test.go -- Test harness for encrypt/decrypt bits
|
||||||
|
//
|
||||||
|
// (c) 2016 Sudhi Herle <sudhi@herle.net>
|
||||||
|
//
|
||||||
|
// Licensing Terms: GPLv2
|
||||||
|
//
|
||||||
|
// If you need a commercial license for this work, please contact
|
||||||
|
// the author.
|
||||||
|
//
|
||||||
|
// This software does not come with any express or implied
|
||||||
|
// warranty; it is provided "as is". No claim is made to its
|
||||||
|
// suitability for any purpose.
|
||||||
|
|
||||||
|
package sign
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// one sender, one receiver no verification of sender
|
||||||
|
func TestSimple(t *testing.T) {
|
||||||
|
assert := newAsserter(t)
|
||||||
|
|
||||||
|
receiver, err := NewKeypair()
|
||||||
|
assert(err == nil, "receiver keypair gen failed: %s", err)
|
||||||
|
|
||||||
|
// cleartext
|
||||||
|
buf := make([]byte, 64*1024)
|
||||||
|
for i := 0; i < len(buf); i++ {
|
||||||
|
buf[i] = byte(i & 0xff)
|
||||||
|
}
|
||||||
|
|
||||||
|
ee, err := NewEncryptor(nil, 4096)
|
||||||
|
assert(err == nil, "encryptor create fail: %s", err)
|
||||||
|
|
||||||
|
err = ee.AddRecipient(&receiver.Pub)
|
||||||
|
assert(err == nil, "can't add recipient: %s", err)
|
||||||
|
|
||||||
|
rd := bytes.NewBuffer(buf)
|
||||||
|
wr := bytes.Buffer{}
|
||||||
|
|
||||||
|
err = ee.Encrypt(rd, &wr)
|
||||||
|
assert(err == nil, "encrypt fail: %s", err)
|
||||||
|
|
||||||
|
rd = bytes.NewBuffer(wr.Bytes())
|
||||||
|
|
||||||
|
dd, err := NewDecryptor(rd)
|
||||||
|
assert(err == nil, "decryptor create fail: %s", err)
|
||||||
|
|
||||||
|
err = dd.SetPrivateKey(&receiver.Sec, nil)
|
||||||
|
assert(err == nil, "decryptor can't add SK: %s", err)
|
||||||
|
|
||||||
|
wr = bytes.Buffer{}
|
||||||
|
err = dd.Decrypt(&wr)
|
||||||
|
assert(err == nil, "decrypt fail: %s", err)
|
||||||
|
|
||||||
|
b := wr.Bytes()
|
||||||
|
assert(len(b) == len(buf), "decrypt length mismatch: exp %d, saw %d", len(buf), len(b))
|
||||||
|
|
||||||
|
assert(byteEq(b, buf), "decrypt content mismatch")
|
||||||
|
}
|
||||||
|
|
||||||
|
// test corrupted header or corrupted input
|
||||||
|
func TestCorrupted(t *testing.T) {
|
||||||
|
assert := newAsserter(t)
|
||||||
|
|
||||||
|
receiver, err := NewKeypair()
|
||||||
|
assert(err == nil, "receiver keypair gen failed: %s", err)
|
||||||
|
|
||||||
|
// cleartext
|
||||||
|
buf := make([]byte, 64*1024)
|
||||||
|
for i := 0; i < len(buf); i++ {
|
||||||
|
buf[i] = byte(i & 0xff)
|
||||||
|
}
|
||||||
|
|
||||||
|
ee, err := NewEncryptor(nil, 4096)
|
||||||
|
assert(err == nil, "encryptor create fail: %s", err)
|
||||||
|
|
||||||
|
err = ee.AddRecipient(&receiver.Pub)
|
||||||
|
assert(err == nil, "can't add recipient: %s", err)
|
||||||
|
|
||||||
|
rd := bytes.NewBuffer(buf)
|
||||||
|
wr := bytes.Buffer{}
|
||||||
|
|
||||||
|
err = ee.Encrypt(rd, &wr)
|
||||||
|
assert(err == nil, "encrypt fail: %s", err)
|
||||||
|
|
||||||
|
rb := wr.Bytes()
|
||||||
|
n := len(rb)
|
||||||
|
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
j := randint() % n
|
||||||
|
rb[j] = byte(randint() & 0xff)
|
||||||
|
}
|
||||||
|
|
||||||
|
rd = bytes.NewBuffer(rb)
|
||||||
|
dd, err := NewDecryptor(rd)
|
||||||
|
assert(err != nil, "decryptor works on bad input")
|
||||||
|
assert(dd == nil, "decryptor not nil for bad input")
|
||||||
|
}
|
||||||
|
|
||||||
|
// one sender, one receiver with verification of sender
|
||||||
|
func TestSenderVerified(t *testing.T) {
|
||||||
|
assert := newAsserter(t)
|
||||||
|
|
||||||
|
sender, err := NewKeypair()
|
||||||
|
assert(err == nil, "sender keypair gen failed: %s", err)
|
||||||
|
|
||||||
|
receiver, err := NewKeypair()
|
||||||
|
assert(err == nil, "receiver keypair gen failed: %s", err)
|
||||||
|
|
||||||
|
// cleartext
|
||||||
|
buf := make([]byte, 64*1024)
|
||||||
|
for i := 0; i < len(buf); i++ {
|
||||||
|
buf[i] = byte(i & 0xff)
|
||||||
|
}
|
||||||
|
|
||||||
|
ee, err := NewEncryptor(&sender.Sec, 4096)
|
||||||
|
assert(err == nil, "encryptor create fail: %s", err)
|
||||||
|
|
||||||
|
err = ee.AddRecipient(&receiver.Pub)
|
||||||
|
assert(err == nil, "can't add recipient: %s", err)
|
||||||
|
|
||||||
|
rd := bytes.NewBuffer(buf)
|
||||||
|
wr := bytes.Buffer{}
|
||||||
|
|
||||||
|
err = ee.Encrypt(rd, &wr)
|
||||||
|
assert(err == nil, "encrypt fail: %s", err)
|
||||||
|
|
||||||
|
rd = bytes.NewBuffer(wr.Bytes())
|
||||||
|
|
||||||
|
dd, err := NewDecryptor(rd)
|
||||||
|
assert(err == nil, "decryptor create fail: %s", err)
|
||||||
|
|
||||||
|
err = dd.SetPrivateKey(&receiver.Sec, &sender.Pub)
|
||||||
|
assert(err == nil, "decryptor can't add SK: %s", err)
|
||||||
|
|
||||||
|
wr = bytes.Buffer{}
|
||||||
|
err = dd.Decrypt(&wr)
|
||||||
|
assert(err == nil, "decrypt fail: %s", err)
|
||||||
|
|
||||||
|
b := wr.Bytes()
|
||||||
|
assert(len(b) == len(buf), "decrypt length mismatch: exp %d, saw %d", len(buf), len(b))
|
||||||
|
|
||||||
|
assert(byteEq(b, buf), "decrypt content mismatch")
|
||||||
|
}
|
||||||
|
|
||||||
|
// one sender, multiple receivers, each decrypting the blob
|
||||||
|
func TestMultiReceiver(t *testing.T) {
|
||||||
|
assert := newAsserter(t)
|
||||||
|
|
||||||
|
sender, err := NewKeypair()
|
||||||
|
assert(err == nil, "sender keypair gen failed: %s", err)
|
||||||
|
|
||||||
|
// cleartext
|
||||||
|
buf := make([]byte, 64*1024)
|
||||||
|
for i := 0; i < len(buf); i++ {
|
||||||
|
buf[i] = byte(i & 0xff)
|
||||||
|
}
|
||||||
|
|
||||||
|
ee, err := NewEncryptor(&sender.Sec, 4096)
|
||||||
|
assert(err == nil, "encryptor create fail: %s", err)
|
||||||
|
|
||||||
|
n := 4
|
||||||
|
rx := make([]*Keypair, n)
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
r, err := NewKeypair()
|
||||||
|
assert(err == nil, "can't make receiver key %d: %s", i, err)
|
||||||
|
rx[i] = r
|
||||||
|
|
||||||
|
err = ee.AddRecipient(&r.Pub)
|
||||||
|
assert(err == nil, "can't add recipient %d: %s", i, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rd := bytes.NewBuffer(buf)
|
||||||
|
wr := bytes.Buffer{}
|
||||||
|
|
||||||
|
err = ee.Encrypt(rd, &wr)
|
||||||
|
assert(err == nil, "encrypt fail: %s", err)
|
||||||
|
|
||||||
|
encBytes := wr.Bytes()
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
rd = bytes.NewBuffer(encBytes)
|
||||||
|
|
||||||
|
dd, err := NewDecryptor(rd)
|
||||||
|
assert(err == nil, "decryptor %d create fail: %s", i, err)
|
||||||
|
|
||||||
|
err = dd.SetPrivateKey(&rx[i].Sec, &sender.Pub)
|
||||||
|
assert(err == nil, "decryptor can't add SK %d: %s", i, err)
|
||||||
|
|
||||||
|
wr = bytes.Buffer{}
|
||||||
|
err = dd.Decrypt(&wr)
|
||||||
|
assert(err == nil, "decrypt %d fail: %s", i, err)
|
||||||
|
|
||||||
|
b := wr.Bytes()
|
||||||
|
assert(len(b) == len(buf), "decrypt %d length mismatch: exp %d, saw %d", i, len(buf), len(b))
|
||||||
|
|
||||||
|
assert(byteEq(b, buf), "decrypt %d content mismatch", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func randint() int {
|
||||||
|
var b [4]byte
|
||||||
|
|
||||||
|
_, err := io.ReadFull(rand.Reader, b[:])
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("can't read 4 rand bytes: %s", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
u := binary.BigEndian.Uint32(b[:])
|
||||||
|
|
||||||
|
return int(u & 0x7fffffff)
|
||||||
|
}
|
33
sign/doc.go
33
sign/doc.go
|
@ -1,33 +0,0 @@
|
||||||
// doc.go -- Documentation for sign & encrypt
|
|
||||||
//
|
|
||||||
// (c) 2016 Sudhi Herle <sudhi@herle.net>
|
|
||||||
//
|
|
||||||
// Licensing Terms: GPLv2
|
|
||||||
//
|
|
||||||
// If you need a commercial license for this work, please contact
|
|
||||||
// the author.
|
|
||||||
//
|
|
||||||
// This software does not come with any express or implied
|
|
||||||
// warranty; it is provided "as is". No claim is made to its
|
|
||||||
// suitability for any purpose.
|
|
||||||
|
|
||||||
// Package sign implements Ed25519 signing, verification on files.
|
|
||||||
// It builds upon golang.org/x/crypto/ed25519 by adding methods
|
|
||||||
// for serializing and deserializing Ed25519 private & public keys.
|
|
||||||
//
|
|
||||||
// It can sign and verify very large files - it prehashes the files
|
|
||||||
// with SHA-512 and then signs the SHA-512 checksum. The keys and signatures
|
|
||||||
// are YAML files and so, human readable.
|
|
||||||
//
|
|
||||||
// It can encrypt files for multiple recipients - each of whom is identified
|
|
||||||
// by their Ed25519 public key. The encryption by default generates ephmeral
|
|
||||||
// Curve25519 keys and creates pair-wise shared secret for each recipient of
|
|
||||||
// the encrypted file. The caller can optionally use a specific secret key
|
|
||||||
// during the encryption process - this has the benefit of also authenticating
|
|
||||||
// the sender (and the receiver can verify the sender if they possess the
|
|
||||||
// corresponding public key).
|
|
||||||
//
|
|
||||||
// The sign, verify, encrypt, decrypt operations can use OpenSSH Ed25519 keys
|
|
||||||
// *or* the keys generated by sigtool. This means, you can send encrypted
|
|
||||||
// files to any recipient identified by their comment in `~/.ssh/authorized_keys`.
|
|
||||||
package sign
|
|
1092
sign/encrypt.go
1092
sign/encrypt.go
File diff suppressed because it is too large
Load diff
|
@ -1,477 +0,0 @@
|
||||||
// crypt_test.go -- Test harness for encrypt/decrypt bits
|
|
||||||
//
|
|
||||||
// (c) 2016 Sudhi Herle <sudhi@herle.net>
|
|
||||||
//
|
|
||||||
// Licensing Terms: GPLv2
|
|
||||||
//
|
|
||||||
// If you need a commercial license for this work, please contact
|
|
||||||
// the author.
|
|
||||||
//
|
|
||||||
// This software does not come with any express or implied
|
|
||||||
// warranty; it is provided "as is". No claim is made to its
|
|
||||||
// suitability for any purpose.
|
|
||||||
|
|
||||||
package sign
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto/rand"
|
|
||||||
"encoding/binary"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Buffer struct {
|
|
||||||
bytes.Buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Buffer) Close() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// one sender, one receiver no verification of sender
|
|
||||||
func TestEncryptSimple(t *testing.T) {
|
|
||||||
assert := newAsserter(t)
|
|
||||||
|
|
||||||
sk, err := NewPrivateKey()
|
|
||||||
assert(err == nil, "SK gen failed: %s", err)
|
|
||||||
|
|
||||||
pk := sk.PublicKey()
|
|
||||||
|
|
||||||
var blkSize int = 1024
|
|
||||||
var size int = (blkSize * 10)
|
|
||||||
|
|
||||||
// cleartext
|
|
||||||
buf := make([]byte, size)
|
|
||||||
for i := 0; i < len(buf); i++ {
|
|
||||||
buf[i] = byte(i & 0xff)
|
|
||||||
}
|
|
||||||
|
|
||||||
ee, err := NewEncryptor(nil, uint64(blkSize))
|
|
||||||
assert(err == nil, "encryptor create fail: %s", err)
|
|
||||||
|
|
||||||
err = ee.AddRecipient(pk)
|
|
||||||
assert(err == nil, "can't add recipient: %s", err)
|
|
||||||
|
|
||||||
rd := bytes.NewBuffer(buf)
|
|
||||||
wr := Buffer{}
|
|
||||||
|
|
||||||
err = ee.Encrypt(rd, &wr)
|
|
||||||
assert(err == nil, "encrypt fail: %s", err)
|
|
||||||
|
|
||||||
rd = bytes.NewBuffer(wr.Bytes())
|
|
||||||
|
|
||||||
dd, err := NewDecryptor(rd)
|
|
||||||
assert(err == nil, "decryptor create fail: %s", err)
|
|
||||||
|
|
||||||
err = dd.SetPrivateKey(sk, nil)
|
|
||||||
assert(err == nil, "decryptor can't add SK: %s", err)
|
|
||||||
|
|
||||||
wr = Buffer{}
|
|
||||||
err = dd.Decrypt(&wr)
|
|
||||||
assert(err == nil, "decrypt fail: %s", err)
|
|
||||||
|
|
||||||
b := wr.Bytes()
|
|
||||||
assert(len(b) == len(buf), "decrypt length mismatch: exp %d, saw %d", len(buf), len(b))
|
|
||||||
|
|
||||||
assert(byteEq(b, buf), "decrypt content mismatch")
|
|
||||||
}
|
|
||||||
|
|
||||||
// one sender, one receiver - small blocks
|
|
||||||
func TestEncryptSmallSizes(t *testing.T) {
|
|
||||||
assert := newAsserter(t)
|
|
||||||
|
|
||||||
sk, err := NewPrivateKey()
|
|
||||||
assert(err == nil, "SK gen failed: %s", err)
|
|
||||||
|
|
||||||
pk := sk.PublicKey()
|
|
||||||
|
|
||||||
var blkSize int = 8
|
|
||||||
var size int = (blkSize * 4)
|
|
||||||
|
|
||||||
// cleartext
|
|
||||||
bigbuf := make([]byte, size)
|
|
||||||
for i := 0; i < len(bigbuf); i++ {
|
|
||||||
bigbuf[i] = byte(i & 0xff)
|
|
||||||
}
|
|
||||||
|
|
||||||
// encrypt progressively larger bufs
|
|
||||||
for i := 1; i < len(bigbuf); i++ {
|
|
||||||
buf := bigbuf[:i]
|
|
||||||
|
|
||||||
ee, err := NewEncryptor(nil, uint64(blkSize))
|
|
||||||
assert(err == nil, "encryptor-%d create fail: %s", i, err)
|
|
||||||
|
|
||||||
err = ee.AddRecipient(pk)
|
|
||||||
assert(err == nil, "encryptor-%d: can't add recipient: %s", i, err)
|
|
||||||
|
|
||||||
rd := bytes.NewBuffer(buf)
|
|
||||||
wr := Buffer{}
|
|
||||||
|
|
||||||
err = ee.Encrypt(rd, &wr)
|
|
||||||
assert(err == nil, "encrypt-%d fail: %s", i, err)
|
|
||||||
|
|
||||||
rd = bytes.NewBuffer(wr.Bytes())
|
|
||||||
|
|
||||||
dd, err := NewDecryptor(rd)
|
|
||||||
assert(err == nil, "decryptor-%d create fail: %s", i, err)
|
|
||||||
|
|
||||||
err = dd.SetPrivateKey(sk, nil)
|
|
||||||
assert(err == nil, "decryptor-%d can't add SK: %s", i, err)
|
|
||||||
|
|
||||||
wr = Buffer{}
|
|
||||||
err = dd.Decrypt(&wr)
|
|
||||||
assert(err == nil, "decrypt-%d fail: %s", i, err)
|
|
||||||
|
|
||||||
b := wr.Bytes()
|
|
||||||
assert(len(b) == len(buf), "decrypt-%d length mismatch: exp %d, saw %d", i, len(buf), len(b))
|
|
||||||
|
|
||||||
assert(byteEq(b, buf), "decrypt-%d content mismatch", i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// test corrupted header or corrupted input
|
|
||||||
func TestEncryptCorrupted(t *testing.T) {
|
|
||||||
assert := newAsserter(t)
|
|
||||||
|
|
||||||
sk, err := NewPrivateKey()
|
|
||||||
assert(err == nil, "SK gen failed: %s", err)
|
|
||||||
|
|
||||||
pk := sk.PublicKey()
|
|
||||||
|
|
||||||
var blkSize int = 1024
|
|
||||||
var size int = (blkSize * 23) + randmod(blkSize)
|
|
||||||
|
|
||||||
// cleartext
|
|
||||||
buf := make([]byte, size)
|
|
||||||
for i := 0; i < len(buf); i++ {
|
|
||||||
buf[i] = byte(i & 0xff)
|
|
||||||
}
|
|
||||||
|
|
||||||
ee, err := NewEncryptor(nil, uint64(blkSize))
|
|
||||||
assert(err == nil, "encryptor create fail: %s", err)
|
|
||||||
|
|
||||||
err = ee.AddRecipient(pk)
|
|
||||||
assert(err == nil, "can't add recipient: %s", err)
|
|
||||||
|
|
||||||
rd := bytes.NewReader(buf)
|
|
||||||
wr := Buffer{}
|
|
||||||
|
|
||||||
err = ee.Encrypt(rd, &wr)
|
|
||||||
assert(err == nil, "encrypt fail: %s", err)
|
|
||||||
|
|
||||||
rb := wr.Bytes()
|
|
||||||
n := len(rb)
|
|
||||||
|
|
||||||
// corrupt the input
|
|
||||||
for i := 0; i < n; i++ {
|
|
||||||
j := randint() % n
|
|
||||||
rb[j] = byte(randint() & 0xff)
|
|
||||||
}
|
|
||||||
|
|
||||||
rd = bytes.NewReader(rb)
|
|
||||||
dd, err := NewDecryptor(rd)
|
|
||||||
assert(err != nil, "decryptor works on bad input")
|
|
||||||
assert(dd == nil, "decryptor not nil for bad input")
|
|
||||||
}
|
|
||||||
|
|
||||||
// one sender, one receiver with verification of sender
|
|
||||||
func TestEncryptSenderVerified(t *testing.T) {
|
|
||||||
assert := newAsserter(t)
|
|
||||||
|
|
||||||
sender, err := NewPrivateKey()
|
|
||||||
assert(err == nil, "sender SK gen failed: %s", err)
|
|
||||||
|
|
||||||
receiver, err := NewPrivateKey()
|
|
||||||
assert(err == nil, "receiver SK gen failed: %s", err)
|
|
||||||
|
|
||||||
var blkSize int = 1024
|
|
||||||
var size int = (blkSize * 23) + randmod(blkSize)
|
|
||||||
|
|
||||||
// cleartext
|
|
||||||
buf := make([]byte, size)
|
|
||||||
for i := 0; i < len(buf); i++ {
|
|
||||||
buf[i] = byte(i & 0xff)
|
|
||||||
}
|
|
||||||
|
|
||||||
ee, err := NewEncryptor(sender, uint64(blkSize))
|
|
||||||
assert(err == nil, "encryptor create fail: %s", err)
|
|
||||||
|
|
||||||
err = ee.AddRecipient(receiver.PublicKey())
|
|
||||||
assert(err == nil, "can't add recipient: %s", err)
|
|
||||||
|
|
||||||
rd := bytes.NewBuffer(buf)
|
|
||||||
wr := Buffer{}
|
|
||||||
|
|
||||||
err = ee.Encrypt(rd, &wr)
|
|
||||||
assert(err == nil, "encrypt fail: %s", err)
|
|
||||||
|
|
||||||
rd = bytes.NewBuffer(wr.Bytes())
|
|
||||||
|
|
||||||
dd, err := NewDecryptor(rd)
|
|
||||||
assert(err == nil, "decryptor create fail: %s", err)
|
|
||||||
|
|
||||||
randkey, err := NewPrivateKey()
|
|
||||||
assert(err == nil, "rand SK gen failed: %s", err)
|
|
||||||
|
|
||||||
// first send a wrong sender PK
|
|
||||||
err = dd.SetPrivateKey(receiver, randkey.PublicKey())
|
|
||||||
assert(err != nil, "decryptor failed to verify sender")
|
|
||||||
|
|
||||||
// then the correct sender PK
|
|
||||||
err = dd.SetPrivateKey(receiver, sender.PublicKey())
|
|
||||||
assert(err == nil, "decryptor can't add SK: %s", err)
|
|
||||||
|
|
||||||
wr = Buffer{}
|
|
||||||
err = dd.Decrypt(&wr)
|
|
||||||
assert(err == nil, "decrypt fail: %s", err)
|
|
||||||
|
|
||||||
b := wr.Bytes()
|
|
||||||
assert(len(b) == len(buf), "decrypt length mismatch: exp %d, saw %d", len(buf), len(b))
|
|
||||||
|
|
||||||
assert(byteEq(b, buf), "decrypt content mismatch")
|
|
||||||
}
|
|
||||||
|
|
||||||
// one sender, multiple receivers, each decrypting the blob
|
|
||||||
func TestEncryptMultiReceiver(t *testing.T) {
|
|
||||||
assert := newAsserter(t)
|
|
||||||
|
|
||||||
sender, err := NewPrivateKey()
|
|
||||||
assert(err == nil, "sender SK gen failed: %s", err)
|
|
||||||
|
|
||||||
var blkSize int = 1024
|
|
||||||
var size int = (blkSize * 23) + randmod(blkSize)
|
|
||||||
|
|
||||||
// cleartext
|
|
||||||
buf := make([]byte, size)
|
|
||||||
for i := 0; i < len(buf); i++ {
|
|
||||||
buf[i] = byte(i & 0xff)
|
|
||||||
}
|
|
||||||
|
|
||||||
ee, err := NewEncryptor(sender, uint64(blkSize))
|
|
||||||
assert(err == nil, "encryptor create fail: %s", err)
|
|
||||||
|
|
||||||
n := 4
|
|
||||||
rx := make([]*PrivateKey, n)
|
|
||||||
for i := 0; i < n; i++ {
|
|
||||||
r, err := NewPrivateKey()
|
|
||||||
assert(err == nil, "can't make receiver SK %d: %s", i, err)
|
|
||||||
rx[i] = r
|
|
||||||
|
|
||||||
err = ee.AddRecipient(r.PublicKey())
|
|
||||||
assert(err == nil, "can't add recipient %d: %s", i, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
rd := bytes.NewBuffer(buf)
|
|
||||||
wr := Buffer{}
|
|
||||||
|
|
||||||
err = ee.Encrypt(rd, &wr)
|
|
||||||
assert(err == nil, "encrypt fail: %s", err)
|
|
||||||
|
|
||||||
encBytes := wr.Bytes()
|
|
||||||
for i := 0; i < n; i++ {
|
|
||||||
rd = bytes.NewBuffer(encBytes)
|
|
||||||
|
|
||||||
dd, err := NewDecryptor(rd)
|
|
||||||
assert(err == nil, "decryptor %d create fail: %s", i, err)
|
|
||||||
|
|
||||||
err = dd.SetPrivateKey(rx[i], sender.PublicKey())
|
|
||||||
assert(err == nil, "decryptor can't add SK %d: %s", i, err)
|
|
||||||
|
|
||||||
wr = Buffer{}
|
|
||||||
err = dd.Decrypt(&wr)
|
|
||||||
assert(err == nil, "decrypt %d fail: %s", i, err)
|
|
||||||
|
|
||||||
b := wr.Bytes()
|
|
||||||
assert(len(b) == len(buf), "decrypt %d length mismatch: exp %d, saw %d", i, len(buf), len(b))
|
|
||||||
|
|
||||||
assert(byteEq(b, buf), "decrypt %d content mismatch", i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test stream write and read
|
|
||||||
func TestStreamIO(t *testing.T) {
|
|
||||||
assert := newAsserter(t)
|
|
||||||
|
|
||||||
receiver, err := NewPrivateKey()
|
|
||||||
assert(err == nil, "receiver keypair gen failed: %s", err)
|
|
||||||
|
|
||||||
var blkSize int = 1024
|
|
||||||
var size int = (blkSize * 10)
|
|
||||||
|
|
||||||
// cleartext
|
|
||||||
buf := make([]byte, size)
|
|
||||||
for i := 0; i < len(buf); i++ {
|
|
||||||
buf[i] = byte(i & 0xff)
|
|
||||||
}
|
|
||||||
|
|
||||||
ee, err := NewEncryptor(nil, uint64(blkSize))
|
|
||||||
assert(err == nil, "encryptor create fail: %s", err)
|
|
||||||
|
|
||||||
err = ee.AddRecipient(receiver.PublicKey())
|
|
||||||
assert(err == nil, "can't add recipient: %s", err)
|
|
||||||
|
|
||||||
wr := Buffer{}
|
|
||||||
wio, err := ee.NewStreamWriter(&wr)
|
|
||||||
assert(err == nil, "can't start stream writer: %s", err)
|
|
||||||
|
|
||||||
// chunksize for writing to stream
|
|
||||||
csize := 19
|
|
||||||
rbuf := buf
|
|
||||||
for len(rbuf) > 0 {
|
|
||||||
m := csize
|
|
||||||
if len(rbuf) < m {
|
|
||||||
m = len(rbuf)
|
|
||||||
}
|
|
||||||
|
|
||||||
n, err := wio.Write(rbuf[:m])
|
|
||||||
assert(err == nil, "stream write failed: %s", err)
|
|
||||||
assert(n == m, "stream write mismatch: exp %d, saw %d", m, n)
|
|
||||||
|
|
||||||
rbuf = rbuf[m:]
|
|
||||||
}
|
|
||||||
err = wio.Close()
|
|
||||||
assert(err == nil, "stream close failed: %s", err)
|
|
||||||
|
|
||||||
_, err = wio.Write(buf[:csize])
|
|
||||||
assert(err != nil, "stream write accepted I/O after close: %s", err)
|
|
||||||
|
|
||||||
rd := bytes.NewBuffer(wr.Bytes())
|
|
||||||
|
|
||||||
dd, err := NewDecryptor(rd)
|
|
||||||
assert(err == nil, "decryptor create fail: %s", err)
|
|
||||||
|
|
||||||
err = dd.SetPrivateKey(receiver, nil)
|
|
||||||
assert(err == nil, "decryptor can't add SK: %s", err)
|
|
||||||
|
|
||||||
rio, err := dd.NewStreamReader()
|
|
||||||
assert(err == nil, "stream reader failed: %s", err)
|
|
||||||
|
|
||||||
rbuf = make([]byte, csize)
|
|
||||||
wr = Buffer{}
|
|
||||||
n := 0
|
|
||||||
for {
|
|
||||||
m, err := rio.Read(rbuf)
|
|
||||||
assert(err == nil || err == io.EOF, "streamread fail: %s", err)
|
|
||||||
|
|
||||||
if m > 0 {
|
|
||||||
wr.Write(rbuf[:m])
|
|
||||||
n += m
|
|
||||||
}
|
|
||||||
if err == io.EOF || m == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
b := wr.Bytes()
|
|
||||||
assert(n == len(b), "streamread: bad buflen; exp %d, saw %d", n, len(b))
|
|
||||||
assert(n == len(buf), "streamread: decrypt len mismatch; exp %d, saw %d", len(buf), n)
|
|
||||||
|
|
||||||
assert(byteEq(b, buf), "decrypt content mismatch")
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test stream write and read with small sizes
|
|
||||||
func TestSmallSizeStreamIO(t *testing.T) {
|
|
||||||
assert := newAsserter(t)
|
|
||||||
|
|
||||||
receiver, err := NewPrivateKey()
|
|
||||||
assert(err == nil, "receiver SK gen failed: %s", err)
|
|
||||||
|
|
||||||
var blkSize int = 8
|
|
||||||
var size int = blkSize * 10
|
|
||||||
|
|
||||||
// cleartext
|
|
||||||
bigbuf := make([]byte, size)
|
|
||||||
for i := 0; i < len(bigbuf); i++ {
|
|
||||||
bigbuf[i] = byte(i & 0xff)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 1; i < len(bigbuf); i++ {
|
|
||||||
buf := bigbuf[:i]
|
|
||||||
t.Logf("small-size-stream: size %d, chunksize %d\n", i, blkSize)
|
|
||||||
|
|
||||||
ee, err := NewEncryptor(nil, uint64(blkSize))
|
|
||||||
assert(err == nil, "encryptor create fail: %s", err)
|
|
||||||
|
|
||||||
err = ee.AddRecipient(receiver.PublicKey())
|
|
||||||
assert(err == nil, "can't add recipient: %s", err)
|
|
||||||
|
|
||||||
wr := Buffer{}
|
|
||||||
wio, err := ee.NewStreamWriter(&wr)
|
|
||||||
assert(err == nil, "can't start stream writer: %s", err)
|
|
||||||
|
|
||||||
// chunksize for writing to stream
|
|
||||||
csize := blkSize - 1
|
|
||||||
rbuf := buf
|
|
||||||
for len(rbuf) > 0 {
|
|
||||||
m := csize
|
|
||||||
if len(rbuf) < m {
|
|
||||||
m = len(rbuf)
|
|
||||||
}
|
|
||||||
|
|
||||||
n, err := wio.Write(rbuf[:m])
|
|
||||||
assert(err == nil, "stream write failed: %s", err)
|
|
||||||
assert(n == m, "stream write mismatch: exp %d, saw %d", m, n)
|
|
||||||
|
|
||||||
rbuf = rbuf[m:]
|
|
||||||
}
|
|
||||||
err = wio.Close()
|
|
||||||
assert(err == nil, "stream close failed: %s", err)
|
|
||||||
|
|
||||||
_, err = wio.Write(buf[:csize])
|
|
||||||
assert(err != nil, "stream write accepted I/O after close: %s", err)
|
|
||||||
|
|
||||||
rd := bytes.NewBuffer(wr.Bytes())
|
|
||||||
|
|
||||||
dd, err := NewDecryptor(rd)
|
|
||||||
assert(err == nil, "decryptor create fail: %s", err)
|
|
||||||
|
|
||||||
err = dd.SetPrivateKey(receiver, nil)
|
|
||||||
assert(err == nil, "decryptor can't add SK: %s", err)
|
|
||||||
|
|
||||||
rio, err := dd.NewStreamReader()
|
|
||||||
assert(err == nil, "stream reader failed: %s", err)
|
|
||||||
|
|
||||||
rbuf = make([]byte, csize)
|
|
||||||
wr = Buffer{}
|
|
||||||
n := 0
|
|
||||||
for {
|
|
||||||
m, err := rio.Read(rbuf)
|
|
||||||
assert(err == nil || err == io.EOF, "streamread fail: %s", err)
|
|
||||||
|
|
||||||
if m > 0 {
|
|
||||||
wr.Write(rbuf[:m])
|
|
||||||
n += m
|
|
||||||
}
|
|
||||||
if err == io.EOF || m == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
b := wr.Bytes()
|
|
||||||
assert(n == len(b), "streamread: bad buflen; exp %d, saw %d", n, len(b))
|
|
||||||
assert(n == len(buf), "streamread: decrypt len mismatch; exp %d, saw %d", len(buf), n)
|
|
||||||
|
|
||||||
assert(byteEq(b, buf), "decrypt content mismatch")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func randint() int {
|
|
||||||
var b [4]byte
|
|
||||||
|
|
||||||
_, err := io.ReadFull(rand.Reader, b[:])
|
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Sprintf("can't read 4 rand bytes: %s", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
u := binary.BigEndian.Uint32(b[:])
|
|
||||||
|
|
||||||
return int(u & 0x7fffffff)
|
|
||||||
}
|
|
||||||
|
|
||||||
func randmod(m int) int {
|
|
||||||
return randint() % m
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
// errors.go - list of all exportable errors in this module
|
|
||||||
//
|
|
||||||
// (c) 2016 Sudhi Herle <sudhi@herle.net>
|
|
||||||
//
|
|
||||||
// Licensing Terms: GPLv2
|
|
||||||
//
|
|
||||||
// If you need a commercial license for this work, please contact
|
|
||||||
// the author.
|
|
||||||
//
|
|
||||||
// This software does not come with any express or implied
|
|
||||||
// warranty; it is provided "as is". No claim is made to its
|
|
||||||
// suitability for any purpose.
|
|
||||||
//
|
|
||||||
|
|
||||||
package sign
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrClosed = errors.New("encrypt: stream already closed")
|
|
||||||
ErrNoKey = errors.New("decrypt: no private key set for decryption")
|
|
||||||
ErrEncStarted = errors.New("encrypt: can't add new recipient after encryption has started")
|
|
||||||
ErrDecStarted = errors.New("decrypt: can't add new recipient after decryption has started")
|
|
||||||
ErrEncIsStream = errors.New("encrypt: can't use Encrypt() after using streaming I/O")
|
|
||||||
ErrNotSigTool = errors.New("decrypt: not a sigtool encrypted file?")
|
|
||||||
ErrHeaderTooBig = errors.New("decrypt: header too large (max 1048576)")
|
|
||||||
ErrHeaderTooSmall = errors.New("decrypt: header too small (min 32)")
|
|
||||||
ErrBadHeader = errors.New("decrypt: header corrupted")
|
|
||||||
ErrNoWrappedKeys = errors.New("decrypt: no wrapped keys in encrypted file")
|
|
||||||
ErrBadKey = errors.New("decrypt: wrong key")
|
|
||||||
ErrBadTrailer = errors.New("decrypt: message integrity failed (bad trailer)")
|
|
||||||
ErrBadSender = errors.New("unwrap: sender verification failed")
|
|
||||||
ErrNoSenderPK = errors.New("unwrap: missing sender public key")
|
|
||||||
|
|
||||||
ErrIncorrectPassword = errors.New("ssh: invalid passphrase")
|
|
||||||
ErrNoPEMFound = errors.New("ssh: no PEM block found")
|
|
||||||
ErrBadPublicKey = errors.New("ssh: malformed public key")
|
|
||||||
ErrKeyTooShort = errors.New("ssh: public key too short")
|
|
||||||
ErrBadTrailers = errors.New("ssh: trailing junk in public key")
|
|
||||||
ErrBadFormat = errors.New("ssh: invalid openssh private key format")
|
|
||||||
ErrBadLength = errors.New("ssh: private key unexpected length")
|
|
||||||
ErrBadPadding = errors.New("ssh: padding not as expected")
|
|
||||||
)
|
|
899
sign/hdr.pb.go
Normal file
899
sign/hdr.pb.go
Normal file
|
@ -0,0 +1,899 @@
|
||||||
|
// Code generated by protoc-gen-gogo. DO NOT EDIT.
|
||||||
|
// source: sign/hdr.proto
|
||||||
|
|
||||||
|
package sign
|
||||||
|
|
||||||
|
import (
|
||||||
|
bytes "bytes"
|
||||||
|
fmt "fmt"
|
||||||
|
proto "github.com/gogo/protobuf/proto"
|
||||||
|
io "io"
|
||||||
|
math "math"
|
||||||
|
math_bits "math/bits"
|
||||||
|
reflect "reflect"
|
||||||
|
strings "strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
var _ = proto.Marshal
|
||||||
|
var _ = fmt.Errorf
|
||||||
|
var _ = math.Inf
|
||||||
|
|
||||||
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
|
// is compatible with the proto package it is being compiled against.
|
||||||
|
// A compilation error at this line likely means your copy of the
|
||||||
|
// proto package needs to be updated.
|
||||||
|
const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
|
||||||
|
|
||||||
|
type Header struct {
|
||||||
|
ChunkSize uint32 `protobuf:"varint,1,opt,name=chunk_size,json=chunkSize,proto3" json:"chunk_size,omitempty"`
|
||||||
|
Salt []byte `protobuf:"bytes,2,opt,name=salt,proto3" json:"salt,omitempty"`
|
||||||
|
Keys []*WrappedKey `protobuf:"bytes,3,rep,name=keys,proto3" json:"keys,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Header) Reset() { *m = Header{} }
|
||||||
|
func (*Header) ProtoMessage() {}
|
||||||
|
func (*Header) Descriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_85aff542c746609f, []int{0}
|
||||||
|
}
|
||||||
|
func (m *Header) XXX_Unmarshal(b []byte) error {
|
||||||
|
return m.Unmarshal(b)
|
||||||
|
}
|
||||||
|
func (m *Header) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
|
if deterministic {
|
||||||
|
return xxx_messageInfo_Header.Marshal(b, m, deterministic)
|
||||||
|
} else {
|
||||||
|
b = b[:cap(b)]
|
||||||
|
n, err := m.MarshalToSizedBuffer(b)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return b[:n], nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (m *Header) XXX_Merge(src proto.Message) {
|
||||||
|
xxx_messageInfo_Header.Merge(m, src)
|
||||||
|
}
|
||||||
|
func (m *Header) XXX_Size() int {
|
||||||
|
return m.Size()
|
||||||
|
}
|
||||||
|
func (m *Header) XXX_DiscardUnknown() {
|
||||||
|
xxx_messageInfo_Header.DiscardUnknown(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
var xxx_messageInfo_Header proto.InternalMessageInfo
|
||||||
|
|
||||||
|
func (m *Header) GetChunkSize() uint32 {
|
||||||
|
if m != nil {
|
||||||
|
return m.ChunkSize
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Header) GetSalt() []byte {
|
||||||
|
if m != nil {
|
||||||
|
return m.Salt
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Header) GetKeys() []*WrappedKey {
|
||||||
|
if m != nil {
|
||||||
|
return m.Keys
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type WrappedKey struct {
|
||||||
|
PkHash []byte `protobuf:"bytes,1,opt,name=pk_hash,json=pkHash,proto3" json:"pk_hash,omitempty"`
|
||||||
|
Pk []byte `protobuf:"bytes,2,opt,name=pk,proto3" json:"pk,omitempty"`
|
||||||
|
Nonce []byte `protobuf:"bytes,3,opt,name=nonce,proto3" json:"nonce,omitempty"`
|
||||||
|
Key []byte `protobuf:"bytes,4,opt,name=key,proto3" json:"key,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *WrappedKey) Reset() { *m = WrappedKey{} }
|
||||||
|
func (*WrappedKey) ProtoMessage() {}
|
||||||
|
func (*WrappedKey) Descriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_85aff542c746609f, []int{1}
|
||||||
|
}
|
||||||
|
func (m *WrappedKey) XXX_Unmarshal(b []byte) error {
|
||||||
|
return m.Unmarshal(b)
|
||||||
|
}
|
||||||
|
func (m *WrappedKey) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
|
if deterministic {
|
||||||
|
return xxx_messageInfo_WrappedKey.Marshal(b, m, deterministic)
|
||||||
|
} else {
|
||||||
|
b = b[:cap(b)]
|
||||||
|
n, err := m.MarshalToSizedBuffer(b)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return b[:n], nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (m *WrappedKey) XXX_Merge(src proto.Message) {
|
||||||
|
xxx_messageInfo_WrappedKey.Merge(m, src)
|
||||||
|
}
|
||||||
|
func (m *WrappedKey) XXX_Size() int {
|
||||||
|
return m.Size()
|
||||||
|
}
|
||||||
|
func (m *WrappedKey) XXX_DiscardUnknown() {
|
||||||
|
xxx_messageInfo_WrappedKey.DiscardUnknown(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
var xxx_messageInfo_WrappedKey proto.InternalMessageInfo
|
||||||
|
|
||||||
|
func (m *WrappedKey) GetPkHash() []byte {
|
||||||
|
if m != nil {
|
||||||
|
return m.PkHash
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *WrappedKey) GetPk() []byte {
|
||||||
|
if m != nil {
|
||||||
|
return m.Pk
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *WrappedKey) GetNonce() []byte {
|
||||||
|
if m != nil {
|
||||||
|
return m.Nonce
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *WrappedKey) GetKey() []byte {
|
||||||
|
if m != nil {
|
||||||
|
return m.Key
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
proto.RegisterType((*Header)(nil), "sign.header")
|
||||||
|
proto.RegisterType((*WrappedKey)(nil), "sign.wrapped_key")
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { proto.RegisterFile("sign/hdr.proto", fileDescriptor_85aff542c746609f) }
|
||||||
|
|
||||||
|
var fileDescriptor_85aff542c746609f = []byte{
|
||||||
|
// 257 bytes of a gzipped FileDescriptorProto
|
||||||
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x4c, 0x90, 0x31, 0x4a, 0xc4, 0x40,
|
||||||
|
0x14, 0x86, 0xe7, 0x25, 0x31, 0xe2, 0xdb, 0x75, 0xd1, 0x41, 0x30, 0x8d, 0x8f, 0xb0, 0x20, 0xa4,
|
||||||
|
0x8a, 0xa0, 0x9e, 0xc0, 0xca, 0x3a, 0xf6, 0x86, 0xec, 0x66, 0x70, 0xc2, 0x48, 0x32, 0x64, 0x56,
|
||||||
|
0x24, 0x5b, 0x79, 0x04, 0x8f, 0xe1, 0x51, 0x2c, 0x53, 0x6e, 0x69, 0x26, 0x8d, 0xe5, 0x1e, 0x41,
|
||||||
|
0x32, 0x5a, 0xd8, 0xfd, 0xff, 0xf7, 0xe0, 0x7d, 0xf0, 0xe3, 0xc2, 0x54, 0x4f, 0xf5, 0x95, 0x2c,
|
||||||
|
0xdb, 0x54, 0xb7, 0xcd, 0xa6, 0xe1, 0xc1, 0xd4, 0x97, 0x2b, 0x0c, 0xa5, 0x28, 0x4a, 0xd1, 0xf2,
|
||||||
|
0x0b, 0xc4, 0xb5, 0x7c, 0xa9, 0x55, 0x6e, 0xaa, 0xad, 0x88, 0x20, 0x86, 0xe4, 0x38, 0x3b, 0x72,
|
||||||
|
0xe4, 0xa1, 0xda, 0x0a, 0xce, 0x31, 0x30, 0xc5, 0xf3, 0x26, 0xf2, 0x62, 0x48, 0xe6, 0x99, 0xcb,
|
||||||
|
0xfc, 0x12, 0x03, 0x25, 0x3a, 0x13, 0xf9, 0xb1, 0x9f, 0xcc, 0xae, 0x4f, 0xd3, 0xe9, 0x63, 0xfa,
|
||||||
|
0xda, 0x16, 0x5a, 0x8b, 0x32, 0x57, 0xa2, 0xcb, 0xdc, 0x79, 0xf9, 0x88, 0xb3, 0x7f, 0x90, 0x9f,
|
||||||
|
0xe3, 0xa1, 0x56, 0xb9, 0x2c, 0x8c, 0x74, 0x96, 0x79, 0x16, 0x6a, 0x75, 0x5f, 0x18, 0xc9, 0x17,
|
||||||
|
0xe8, 0x69, 0xf5, 0x27, 0xf0, 0xb4, 0xe2, 0x67, 0x78, 0x50, 0x37, 0xf5, 0x5a, 0x44, 0xbe, 0x43,
|
||||||
|
0xbf, 0x85, 0x9f, 0xa0, 0xaf, 0x44, 0x17, 0x05, 0x8e, 0x4d, 0xf1, 0xee, 0xb6, 0x1f, 0x88, 0xed,
|
||||||
|
0x06, 0x62, 0xfb, 0x81, 0xe0, 0xcd, 0x12, 0x7c, 0x58, 0x82, 0x4f, 0x4b, 0xd0, 0x5b, 0x82, 0x2f,
|
||||||
|
0x4b, 0xf0, 0x6d, 0x89, 0xed, 0x2d, 0xc1, 0xfb, 0x48, 0xac, 0x1f, 0x89, 0xed, 0x46, 0x62, 0xab,
|
||||||
|
0xd0, 0xcd, 0x70, 0xf3, 0x13, 0x00, 0x00, 0xff, 0xff, 0xde, 0xf2, 0x28, 0xc0, 0x18, 0x01, 0x00,
|
||||||
|
0x00,
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *Header) Equal(that interface{}) bool {
|
||||||
|
if that == nil {
|
||||||
|
return this == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
that1, ok := that.(*Header)
|
||||||
|
if !ok {
|
||||||
|
that2, ok := that.(Header)
|
||||||
|
if ok {
|
||||||
|
that1 = &that2
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if that1 == nil {
|
||||||
|
return this == nil
|
||||||
|
} else if this == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if this.ChunkSize != that1.ChunkSize {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !bytes.Equal(this.Salt, that1.Salt) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if len(this.Keys) != len(that1.Keys) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i := range this.Keys {
|
||||||
|
if !this.Keys[i].Equal(that1.Keys[i]) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
func (this *WrappedKey) Equal(that interface{}) bool {
|
||||||
|
if that == nil {
|
||||||
|
return this == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
that1, ok := that.(*WrappedKey)
|
||||||
|
if !ok {
|
||||||
|
that2, ok := that.(WrappedKey)
|
||||||
|
if ok {
|
||||||
|
that1 = &that2
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if that1 == nil {
|
||||||
|
return this == nil
|
||||||
|
} else if this == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !bytes.Equal(this.PkHash, that1.PkHash) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !bytes.Equal(this.Pk, that1.Pk) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !bytes.Equal(this.Nonce, that1.Nonce) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !bytes.Equal(this.Key, that1.Key) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
func (this *Header) GoString() string {
|
||||||
|
if this == nil {
|
||||||
|
return "nil"
|
||||||
|
}
|
||||||
|
s := make([]string, 0, 7)
|
||||||
|
s = append(s, "&sign.Header{")
|
||||||
|
s = append(s, "ChunkSize: "+fmt.Sprintf("%#v", this.ChunkSize)+",\n")
|
||||||
|
s = append(s, "Salt: "+fmt.Sprintf("%#v", this.Salt)+",\n")
|
||||||
|
if this.Keys != nil {
|
||||||
|
s = append(s, "Keys: "+fmt.Sprintf("%#v", this.Keys)+",\n")
|
||||||
|
}
|
||||||
|
s = append(s, "}")
|
||||||
|
return strings.Join(s, "")
|
||||||
|
}
|
||||||
|
func (this *WrappedKey) GoString() string {
|
||||||
|
if this == nil {
|
||||||
|
return "nil"
|
||||||
|
}
|
||||||
|
s := make([]string, 0, 8)
|
||||||
|
s = append(s, "&sign.WrappedKey{")
|
||||||
|
s = append(s, "PkHash: "+fmt.Sprintf("%#v", this.PkHash)+",\n")
|
||||||
|
s = append(s, "Pk: "+fmt.Sprintf("%#v", this.Pk)+",\n")
|
||||||
|
s = append(s, "Nonce: "+fmt.Sprintf("%#v", this.Nonce)+",\n")
|
||||||
|
s = append(s, "Key: "+fmt.Sprintf("%#v", this.Key)+",\n")
|
||||||
|
s = append(s, "}")
|
||||||
|
return strings.Join(s, "")
|
||||||
|
}
|
||||||
|
func valueToGoStringHdr(v interface{}, typ string) string {
|
||||||
|
rv := reflect.ValueOf(v)
|
||||||
|
if rv.IsNil() {
|
||||||
|
return "nil"
|
||||||
|
}
|
||||||
|
pv := reflect.Indirect(rv).Interface()
|
||||||
|
return fmt.Sprintf("func(v %v) *%v { return &v } ( %#v )", typ, typ, pv)
|
||||||
|
}
|
||||||
|
func (m *Header) Marshal() (dAtA []byte, err error) {
|
||||||
|
size := m.Size()
|
||||||
|
dAtA = make([]byte, size)
|
||||||
|
n, err := m.MarshalToSizedBuffer(dAtA[:size])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return dAtA[:n], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Header) MarshalTo(dAtA []byte) (int, error) {
|
||||||
|
size := m.Size()
|
||||||
|
return m.MarshalToSizedBuffer(dAtA[:size])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Header) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||||
|
i := len(dAtA)
|
||||||
|
_ = i
|
||||||
|
var l int
|
||||||
|
_ = l
|
||||||
|
if len(m.Keys) > 0 {
|
||||||
|
for iNdEx := len(m.Keys) - 1; iNdEx >= 0; iNdEx-- {
|
||||||
|
{
|
||||||
|
size, err := m.Keys[iNdEx].MarshalToSizedBuffer(dAtA[:i])
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
i -= size
|
||||||
|
i = encodeVarintHdr(dAtA, i, uint64(size))
|
||||||
|
}
|
||||||
|
i--
|
||||||
|
dAtA[i] = 0x1a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(m.Salt) > 0 {
|
||||||
|
i -= len(m.Salt)
|
||||||
|
copy(dAtA[i:], m.Salt)
|
||||||
|
i = encodeVarintHdr(dAtA, i, uint64(len(m.Salt)))
|
||||||
|
i--
|
||||||
|
dAtA[i] = 0x12
|
||||||
|
}
|
||||||
|
if m.ChunkSize != 0 {
|
||||||
|
i = encodeVarintHdr(dAtA, i, uint64(m.ChunkSize))
|
||||||
|
i--
|
||||||
|
dAtA[i] = 0x8
|
||||||
|
}
|
||||||
|
return len(dAtA) - i, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *WrappedKey) Marshal() (dAtA []byte, err error) {
|
||||||
|
size := m.Size()
|
||||||
|
dAtA = make([]byte, size)
|
||||||
|
n, err := m.MarshalToSizedBuffer(dAtA[:size])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return dAtA[:n], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *WrappedKey) MarshalTo(dAtA []byte) (int, error) {
|
||||||
|
size := m.Size()
|
||||||
|
return m.MarshalToSizedBuffer(dAtA[:size])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *WrappedKey) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||||
|
i := len(dAtA)
|
||||||
|
_ = i
|
||||||
|
var l int
|
||||||
|
_ = l
|
||||||
|
if len(m.Key) > 0 {
|
||||||
|
i -= len(m.Key)
|
||||||
|
copy(dAtA[i:], m.Key)
|
||||||
|
i = encodeVarintHdr(dAtA, i, uint64(len(m.Key)))
|
||||||
|
i--
|
||||||
|
dAtA[i] = 0x22
|
||||||
|
}
|
||||||
|
if len(m.Nonce) > 0 {
|
||||||
|
i -= len(m.Nonce)
|
||||||
|
copy(dAtA[i:], m.Nonce)
|
||||||
|
i = encodeVarintHdr(dAtA, i, uint64(len(m.Nonce)))
|
||||||
|
i--
|
||||||
|
dAtA[i] = 0x1a
|
||||||
|
}
|
||||||
|
if len(m.Pk) > 0 {
|
||||||
|
i -= len(m.Pk)
|
||||||
|
copy(dAtA[i:], m.Pk)
|
||||||
|
i = encodeVarintHdr(dAtA, i, uint64(len(m.Pk)))
|
||||||
|
i--
|
||||||
|
dAtA[i] = 0x12
|
||||||
|
}
|
||||||
|
if len(m.PkHash) > 0 {
|
||||||
|
i -= len(m.PkHash)
|
||||||
|
copy(dAtA[i:], m.PkHash)
|
||||||
|
i = encodeVarintHdr(dAtA, i, uint64(len(m.PkHash)))
|
||||||
|
i--
|
||||||
|
dAtA[i] = 0xa
|
||||||
|
}
|
||||||
|
return len(dAtA) - i, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeVarintHdr(dAtA []byte, offset int, v uint64) int {
|
||||||
|
offset -= sovHdr(v)
|
||||||
|
base := offset
|
||||||
|
for v >= 1<<7 {
|
||||||
|
dAtA[offset] = uint8(v&0x7f | 0x80)
|
||||||
|
v >>= 7
|
||||||
|
offset++
|
||||||
|
}
|
||||||
|
dAtA[offset] = uint8(v)
|
||||||
|
return base
|
||||||
|
}
|
||||||
|
func (m *Header) Size() (n int) {
|
||||||
|
if m == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
var l int
|
||||||
|
_ = l
|
||||||
|
if m.ChunkSize != 0 {
|
||||||
|
n += 1 + sovHdr(uint64(m.ChunkSize))
|
||||||
|
}
|
||||||
|
l = len(m.Salt)
|
||||||
|
if l > 0 {
|
||||||
|
n += 1 + l + sovHdr(uint64(l))
|
||||||
|
}
|
||||||
|
if len(m.Keys) > 0 {
|
||||||
|
for _, e := range m.Keys {
|
||||||
|
l = e.Size()
|
||||||
|
n += 1 + l + sovHdr(uint64(l))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *WrappedKey) Size() (n int) {
|
||||||
|
if m == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
var l int
|
||||||
|
_ = l
|
||||||
|
l = len(m.PkHash)
|
||||||
|
if l > 0 {
|
||||||
|
n += 1 + l + sovHdr(uint64(l))
|
||||||
|
}
|
||||||
|
l = len(m.Pk)
|
||||||
|
if l > 0 {
|
||||||
|
n += 1 + l + sovHdr(uint64(l))
|
||||||
|
}
|
||||||
|
l = len(m.Nonce)
|
||||||
|
if l > 0 {
|
||||||
|
n += 1 + l + sovHdr(uint64(l))
|
||||||
|
}
|
||||||
|
l = len(m.Key)
|
||||||
|
if l > 0 {
|
||||||
|
n += 1 + l + sovHdr(uint64(l))
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func sovHdr(x uint64) (n int) {
|
||||||
|
return (math_bits.Len64(x|1) + 6) / 7
|
||||||
|
}
|
||||||
|
func sozHdr(x uint64) (n int) {
|
||||||
|
return sovHdr(uint64((x << 1) ^ uint64((int64(x) >> 63))))
|
||||||
|
}
|
||||||
|
func (this *Header) String() string {
|
||||||
|
if this == nil {
|
||||||
|
return "nil"
|
||||||
|
}
|
||||||
|
repeatedStringForKeys := "[]*WrappedKey{"
|
||||||
|
for _, f := range this.Keys {
|
||||||
|
repeatedStringForKeys += strings.Replace(fmt.Sprintf("%v", f), "WrappedKey", "WrappedKey", 1) + ","
|
||||||
|
}
|
||||||
|
repeatedStringForKeys += "}"
|
||||||
|
s := strings.Join([]string{`&Header{`,
|
||||||
|
`ChunkSize:` + fmt.Sprintf("%v", this.ChunkSize) + `,`,
|
||||||
|
`Salt:` + fmt.Sprintf("%v", this.Salt) + `,`,
|
||||||
|
`Keys:` + repeatedStringForKeys + `,`,
|
||||||
|
`}`,
|
||||||
|
}, "")
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
func (this *WrappedKey) String() string {
|
||||||
|
if this == nil {
|
||||||
|
return "nil"
|
||||||
|
}
|
||||||
|
s := strings.Join([]string{`&WrappedKey{`,
|
||||||
|
`PkHash:` + fmt.Sprintf("%v", this.PkHash) + `,`,
|
||||||
|
`Pk:` + fmt.Sprintf("%v", this.Pk) + `,`,
|
||||||
|
`Nonce:` + fmt.Sprintf("%v", this.Nonce) + `,`,
|
||||||
|
`Key:` + fmt.Sprintf("%v", this.Key) + `,`,
|
||||||
|
`}`,
|
||||||
|
}, "")
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
func valueToStringHdr(v interface{}) string {
|
||||||
|
rv := reflect.ValueOf(v)
|
||||||
|
if rv.IsNil() {
|
||||||
|
return "nil"
|
||||||
|
}
|
||||||
|
pv := reflect.Indirect(rv).Interface()
|
||||||
|
return fmt.Sprintf("*%v", pv)
|
||||||
|
}
|
||||||
|
func (m *Header) Unmarshal(dAtA []byte) error {
|
||||||
|
l := len(dAtA)
|
||||||
|
iNdEx := 0
|
||||||
|
for iNdEx < l {
|
||||||
|
preIndex := iNdEx
|
||||||
|
var wire uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowHdr
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
wire |= uint64(b&0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fieldNum := int32(wire >> 3)
|
||||||
|
wireType := int(wire & 0x7)
|
||||||
|
if wireType == 4 {
|
||||||
|
return fmt.Errorf("proto: header: wiretype end group for non-group")
|
||||||
|
}
|
||||||
|
if fieldNum <= 0 {
|
||||||
|
return fmt.Errorf("proto: header: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||||
|
}
|
||||||
|
switch fieldNum {
|
||||||
|
case 1:
|
||||||
|
if wireType != 0 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field ChunkSize", wireType)
|
||||||
|
}
|
||||||
|
m.ChunkSize = 0
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowHdr
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
m.ChunkSize |= uint32(b&0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 2:
|
||||||
|
if wireType != 2 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field Salt", wireType)
|
||||||
|
}
|
||||||
|
var byteLen int
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowHdr
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
byteLen |= int(b&0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if byteLen < 0 {
|
||||||
|
return ErrInvalidLengthHdr
|
||||||
|
}
|
||||||
|
postIndex := iNdEx + byteLen
|
||||||
|
if postIndex < 0 {
|
||||||
|
return ErrInvalidLengthHdr
|
||||||
|
}
|
||||||
|
if postIndex > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
m.Salt = append(m.Salt[:0], dAtA[iNdEx:postIndex]...)
|
||||||
|
if m.Salt == nil {
|
||||||
|
m.Salt = []byte{}
|
||||||
|
}
|
||||||
|
iNdEx = postIndex
|
||||||
|
case 3:
|
||||||
|
if wireType != 2 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field Keys", wireType)
|
||||||
|
}
|
||||||
|
var msglen int
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowHdr
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
msglen |= int(b&0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if msglen < 0 {
|
||||||
|
return ErrInvalidLengthHdr
|
||||||
|
}
|
||||||
|
postIndex := iNdEx + msglen
|
||||||
|
if postIndex < 0 {
|
||||||
|
return ErrInvalidLengthHdr
|
||||||
|
}
|
||||||
|
if postIndex > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
m.Keys = append(m.Keys, &WrappedKey{})
|
||||||
|
if err := m.Keys[len(m.Keys)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
iNdEx = postIndex
|
||||||
|
default:
|
||||||
|
iNdEx = preIndex
|
||||||
|
skippy, err := skipHdr(dAtA[iNdEx:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if skippy < 0 {
|
||||||
|
return ErrInvalidLengthHdr
|
||||||
|
}
|
||||||
|
if (iNdEx + skippy) < 0 {
|
||||||
|
return ErrInvalidLengthHdr
|
||||||
|
}
|
||||||
|
if (iNdEx + skippy) > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
iNdEx += skippy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if iNdEx > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (m *WrappedKey) Unmarshal(dAtA []byte) error {
|
||||||
|
l := len(dAtA)
|
||||||
|
iNdEx := 0
|
||||||
|
for iNdEx < l {
|
||||||
|
preIndex := iNdEx
|
||||||
|
var wire uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowHdr
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
wire |= uint64(b&0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fieldNum := int32(wire >> 3)
|
||||||
|
wireType := int(wire & 0x7)
|
||||||
|
if wireType == 4 {
|
||||||
|
return fmt.Errorf("proto: wrapped_key: wiretype end group for non-group")
|
||||||
|
}
|
||||||
|
if fieldNum <= 0 {
|
||||||
|
return fmt.Errorf("proto: wrapped_key: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||||
|
}
|
||||||
|
switch fieldNum {
|
||||||
|
case 1:
|
||||||
|
if wireType != 2 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field PkHash", wireType)
|
||||||
|
}
|
||||||
|
var byteLen int
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowHdr
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
byteLen |= int(b&0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if byteLen < 0 {
|
||||||
|
return ErrInvalidLengthHdr
|
||||||
|
}
|
||||||
|
postIndex := iNdEx + byteLen
|
||||||
|
if postIndex < 0 {
|
||||||
|
return ErrInvalidLengthHdr
|
||||||
|
}
|
||||||
|
if postIndex > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
m.PkHash = append(m.PkHash[:0], dAtA[iNdEx:postIndex]...)
|
||||||
|
if m.PkHash == nil {
|
||||||
|
m.PkHash = []byte{}
|
||||||
|
}
|
||||||
|
iNdEx = postIndex
|
||||||
|
case 2:
|
||||||
|
if wireType != 2 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field Pk", wireType)
|
||||||
|
}
|
||||||
|
var byteLen int
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowHdr
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
byteLen |= int(b&0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if byteLen < 0 {
|
||||||
|
return ErrInvalidLengthHdr
|
||||||
|
}
|
||||||
|
postIndex := iNdEx + byteLen
|
||||||
|
if postIndex < 0 {
|
||||||
|
return ErrInvalidLengthHdr
|
||||||
|
}
|
||||||
|
if postIndex > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
m.Pk = append(m.Pk[:0], dAtA[iNdEx:postIndex]...)
|
||||||
|
if m.Pk == nil {
|
||||||
|
m.Pk = []byte{}
|
||||||
|
}
|
||||||
|
iNdEx = postIndex
|
||||||
|
case 3:
|
||||||
|
if wireType != 2 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field Nonce", wireType)
|
||||||
|
}
|
||||||
|
var byteLen int
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowHdr
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
byteLen |= int(b&0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if byteLen < 0 {
|
||||||
|
return ErrInvalidLengthHdr
|
||||||
|
}
|
||||||
|
postIndex := iNdEx + byteLen
|
||||||
|
if postIndex < 0 {
|
||||||
|
return ErrInvalidLengthHdr
|
||||||
|
}
|
||||||
|
if postIndex > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
m.Nonce = append(m.Nonce[:0], dAtA[iNdEx:postIndex]...)
|
||||||
|
if m.Nonce == nil {
|
||||||
|
m.Nonce = []byte{}
|
||||||
|
}
|
||||||
|
iNdEx = postIndex
|
||||||
|
case 4:
|
||||||
|
if wireType != 2 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType)
|
||||||
|
}
|
||||||
|
var byteLen int
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowHdr
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
byteLen |= int(b&0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if byteLen < 0 {
|
||||||
|
return ErrInvalidLengthHdr
|
||||||
|
}
|
||||||
|
postIndex := iNdEx + byteLen
|
||||||
|
if postIndex < 0 {
|
||||||
|
return ErrInvalidLengthHdr
|
||||||
|
}
|
||||||
|
if postIndex > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
m.Key = append(m.Key[:0], dAtA[iNdEx:postIndex]...)
|
||||||
|
if m.Key == nil {
|
||||||
|
m.Key = []byte{}
|
||||||
|
}
|
||||||
|
iNdEx = postIndex
|
||||||
|
default:
|
||||||
|
iNdEx = preIndex
|
||||||
|
skippy, err := skipHdr(dAtA[iNdEx:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if skippy < 0 {
|
||||||
|
return ErrInvalidLengthHdr
|
||||||
|
}
|
||||||
|
if (iNdEx + skippy) < 0 {
|
||||||
|
return ErrInvalidLengthHdr
|
||||||
|
}
|
||||||
|
if (iNdEx + skippy) > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
iNdEx += skippy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if iNdEx > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func skipHdr(dAtA []byte) (n int, err error) {
|
||||||
|
l := len(dAtA)
|
||||||
|
iNdEx := 0
|
||||||
|
depth := 0
|
||||||
|
for iNdEx < l {
|
||||||
|
var wire uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return 0, ErrIntOverflowHdr
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return 0, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
wire |= (uint64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wireType := int(wire & 0x7)
|
||||||
|
switch wireType {
|
||||||
|
case 0:
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return 0, ErrIntOverflowHdr
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return 0, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
iNdEx++
|
||||||
|
if dAtA[iNdEx-1] < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 1:
|
||||||
|
iNdEx += 8
|
||||||
|
case 2:
|
||||||
|
var length int
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return 0, ErrIntOverflowHdr
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return 0, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
length |= (int(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if length < 0 {
|
||||||
|
return 0, ErrInvalidLengthHdr
|
||||||
|
}
|
||||||
|
iNdEx += length
|
||||||
|
case 3:
|
||||||
|
depth++
|
||||||
|
case 4:
|
||||||
|
if depth == 0 {
|
||||||
|
return 0, ErrUnexpectedEndOfGroupHdr
|
||||||
|
}
|
||||||
|
depth--
|
||||||
|
case 5:
|
||||||
|
iNdEx += 4
|
||||||
|
default:
|
||||||
|
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
|
||||||
|
}
|
||||||
|
if iNdEx < 0 {
|
||||||
|
return 0, ErrInvalidLengthHdr
|
||||||
|
}
|
||||||
|
if depth == 0 {
|
||||||
|
return iNdEx, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0, io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidLengthHdr = fmt.Errorf("proto: negative length found during unmarshaling")
|
||||||
|
ErrIntOverflowHdr = fmt.Errorf("proto: integer overflow")
|
||||||
|
ErrUnexpectedEndOfGroupHdr = fmt.Errorf("proto: unexpected end of group")
|
||||||
|
)
|
23
sign/hdr.proto
Normal file
23
sign/hdr.proto
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
syntax="proto3";
|
||||||
|
|
||||||
|
//import "gogoproto/gogo.proto"
|
||||||
|
|
||||||
|
package sign;
|
||||||
|
|
||||||
|
//option (gogoproto.marshaler_all) = true;
|
||||||
|
//option (gogoproto.sizer_all) = true;
|
||||||
|
//option (gogoproto.unmarshaler_all) = true;
|
||||||
|
//option (gogoproto.goproto_getters_all) = false;
|
||||||
|
|
||||||
|
message header {
|
||||||
|
uint32 chunk_size = 1;
|
||||||
|
bytes salt = 2;
|
||||||
|
repeated wrapped_key keys = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message wrapped_key {
|
||||||
|
bytes pk_hash = 1; // hash of Ed25519 PK
|
||||||
|
bytes pk = 2; // curve25519 PK
|
||||||
|
bytes nonce = 3; // AEAD nonce
|
||||||
|
bytes key = 4; // AEAD encrypted key
|
||||||
|
}
|
|
@ -1,67 +0,0 @@
|
||||||
// iomisc.go -- misc i/o functions
|
|
||||||
//
|
|
||||||
// (c) 2016 Sudhi Herle <sudhi@herle.net>
|
|
||||||
//
|
|
||||||
// Licensing Terms: GPLv2
|
|
||||||
//
|
|
||||||
// If you need a commercial license for this work, please contact
|
|
||||||
// the author.
|
|
||||||
//
|
|
||||||
// This software does not come with any express or implied
|
|
||||||
// warranty; it is provided "as is". No claim is made to its
|
|
||||||
// suitability for any purpose.
|
|
||||||
|
|
||||||
package sign
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/binary"
|
|
||||||
"fmt"
|
|
||||||
"github.com/opencoff/go-fio"
|
|
||||||
"github.com/opencoff/go-mmap"
|
|
||||||
"hash"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Simple function to reliably write data to a file.
|
|
||||||
// Does MORE than ioutil.WriteFile() - in that it doesn't trash the
|
|
||||||
// existing file with an incomplete write.
|
|
||||||
func writeFile(fn string, b []byte, ovwrite bool, mode uint32) error {
|
|
||||||
var opts uint32
|
|
||||||
if ovwrite {
|
|
||||||
opts |= fio.OPT_OVERWRITE
|
|
||||||
}
|
|
||||||
sf, err := fio.NewSafeFile(fn, opts, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.FileMode(mode))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer sf.Abort()
|
|
||||||
if _, err = sf.Write(b); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return sf.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate file checksum out of hash function h
|
|
||||||
func fileCksum(fn string, h hash.Hash) ([]byte, error) {
|
|
||||||
fd, err := os.Open(fn)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("can't open %s: %s", fn, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
defer fd.Close()
|
|
||||||
|
|
||||||
sz, err := mmap.Reader(fd, func(b []byte) error {
|
|
||||||
h.Write(b)
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var b [8]byte
|
|
||||||
binary.BigEndian.PutUint64(b[:], uint64(sz))
|
|
||||||
h.Write(b[:])
|
|
||||||
|
|
||||||
return h.Sum(nil)[:], nil
|
|
||||||
}
|
|
529
sign/keys.go
529
sign/keys.go
|
@ -1,529 +0,0 @@
|
||||||
// keys.go -- Ed25519 keys management
|
|
||||||
//
|
|
||||||
// (c) 2016 Sudhi Herle <sudhi@herle.net>
|
|
||||||
//
|
|
||||||
// Licensing Terms: GPLv2
|
|
||||||
//
|
|
||||||
// If you need a commercial license for this work, please contact
|
|
||||||
// the author.
|
|
||||||
//
|
|
||||||
// This software does not come with any express or implied
|
|
||||||
// warranty; it is provided "as is". No claim is made to its
|
|
||||||
// suitability for any purpose.
|
|
||||||
|
|
||||||
// This file implements:
|
|
||||||
// - key generation, and key I/O
|
|
||||||
// - sign/verify of files and byte strings
|
|
||||||
|
|
||||||
package sign
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto/aes"
|
|
||||||
"crypto/cipher"
|
|
||||||
"crypto/rand"
|
|
||||||
"crypto/sha256"
|
|
||||||
"crypto/sha512"
|
|
||||||
"encoding/base64"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"math/big"
|
|
||||||
|
|
||||||
Ed "crypto/ed25519"
|
|
||||||
|
|
||||||
"golang.org/x/crypto/scrypt"
|
|
||||||
"gopkg.in/yaml.v3"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Private Ed25519 key
|
|
||||||
type PrivateKey struct {
|
|
||||||
Sk []byte
|
|
||||||
|
|
||||||
// Encryption key: Curve25519 point corresponding to this Ed25519 key
|
|
||||||
ck []byte
|
|
||||||
|
|
||||||
// Cached copy of the public key
|
|
||||||
pk *PublicKey
|
|
||||||
}
|
|
||||||
|
|
||||||
// Public Ed25519 key
|
|
||||||
type PublicKey struct {
|
|
||||||
Pk []byte
|
|
||||||
|
|
||||||
// Comment string
|
|
||||||
Comment string
|
|
||||||
|
|
||||||
// Curve25519 point corresponding to this Ed25519 key
|
|
||||||
ck []byte
|
|
||||||
|
|
||||||
hash []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// Length of Ed25519 Public Key Hash
|
|
||||||
const PKHashLength = 16
|
|
||||||
|
|
||||||
// constants we use in this module
|
|
||||||
const (
|
|
||||||
// Scrypt parameters
|
|
||||||
_N int = 1 << 19
|
|
||||||
_r int = 8
|
|
||||||
_p int = 1
|
|
||||||
|
|
||||||
// Algorithm used in the encrypted private key
|
|
||||||
sk_algo = "scrypt-sha256"
|
|
||||||
sig_algo = "sha512-ed25519"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Encrypted Private key
|
|
||||||
type serializedPrivKey struct {
|
|
||||||
Comment string `yaml:"comment,omitempty"`
|
|
||||||
|
|
||||||
// Encrypted Sk
|
|
||||||
Esk string `yaml:"esk"`
|
|
||||||
Salt string `yaml:"salt,omitempty"`
|
|
||||||
|
|
||||||
// Algorithm used for checksum and KDF
|
|
||||||
Algo string `yaml:"algo,omitempty"`
|
|
||||||
|
|
||||||
// These are params for scrypt.Key()
|
|
||||||
// CPU Cost parameter; must be a power of 2
|
|
||||||
N int `yaml:"Z,flow,omitempty"`
|
|
||||||
|
|
||||||
// r * p should be less than 2^30
|
|
||||||
R int `yaml:"r,flow,omitempty"`
|
|
||||||
P int `yaml:"p,flow,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// serialized representation of public key
|
|
||||||
type serializedPubKey struct {
|
|
||||||
Comment string `yaml:"comment,omitempty"`
|
|
||||||
Pk string `yaml:"pk"`
|
|
||||||
Hash string `yaml:"hash"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serialized signature
|
|
||||||
type signature struct {
|
|
||||||
Comment string `yaml:"comment,omitempty"`
|
|
||||||
Pkhash string `yaml:"pkhash,omitempty"`
|
|
||||||
Signature string `yaml:"signature"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// given a public key, generate a deterministic short-hash of it.
|
|
||||||
func pkhash(pk []byte) []byte {
|
|
||||||
z := sha256.Sum256(pk)
|
|
||||||
return z[:PKHashLength]
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewPrivateKey generates a new Ed25519 private key
|
|
||||||
func NewPrivateKey() (*PrivateKey, error) {
|
|
||||||
pkb, skb, err := Ed.GenerateKey(rand.Reader)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
sk := &PrivateKey{
|
|
||||||
Sk: []byte(skb),
|
|
||||||
pk: &PublicKey{
|
|
||||||
Pk: []byte(pkb),
|
|
||||||
hash: pkhash([]byte(pkb)),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return sk, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read the private key in 'fn', optionally decrypting it using
|
|
||||||
// password 'pw' and create new instance of PrivateKey
|
|
||||||
func ReadPrivateKey(fn string, getpw func() ([]byte, error)) (*PrivateKey, error) {
|
|
||||||
yml, err := ioutil.ReadFile(fn)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var sk PrivateKey
|
|
||||||
if err = sk.UnmarshalBinary(yml, getpw); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &sk, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make a private key from bytes 'yml' using optional caller provided
|
|
||||||
// getpw() function to read the password if needed.
|
|
||||||
// are assumed to be serialized version of the private key.
|
|
||||||
func MakePrivateKey(yml []byte, getpw func() ([]byte, error)) (*PrivateKey, error) {
|
|
||||||
var sk PrivateKey
|
|
||||||
|
|
||||||
err := sk.UnmarshalBinary(yml, getpw)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &sk, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// make a PrivateKey from a byte array containing ed25519 raw SK
|
|
||||||
func makePrivateKeyFromBytes(sk *PrivateKey, buf []byte) error {
|
|
||||||
if len(buf) != 64 {
|
|
||||||
return fmt.Errorf("private key is malformed (len %d!)", len(buf))
|
|
||||||
}
|
|
||||||
|
|
||||||
skb := make([]byte, 64)
|
|
||||||
copy(skb, buf)
|
|
||||||
|
|
||||||
edsk := Ed.PrivateKey(skb)
|
|
||||||
edpk := edsk.Public().(Ed.PublicKey)
|
|
||||||
|
|
||||||
pk := &PublicKey{
|
|
||||||
Pk: []byte(edpk),
|
|
||||||
hash: pkhash([]byte(edpk)),
|
|
||||||
}
|
|
||||||
sk.Sk = skb
|
|
||||||
sk.pk = pk
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make a private key from 64-bytes of extended Ed25519 key
|
|
||||||
func PrivateKeyFromBytes(buf []byte) (*PrivateKey, error) {
|
|
||||||
var sk PrivateKey
|
|
||||||
if err := makePrivateKeyFromBytes(&sk, buf); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &sk, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Given a secret key, return the corresponding Public Key
|
|
||||||
func (sk *PrivateKey) PublicKey() *PublicKey {
|
|
||||||
return sk.pk
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert an Ed25519 Private Key to Curve25519 Private key
|
|
||||||
func (sk *PrivateKey) ToCurve25519SK() []byte {
|
|
||||||
if sk.ck == nil {
|
|
||||||
var ek [64]byte
|
|
||||||
|
|
||||||
h := sha512.New()
|
|
||||||
h.Write(sk.Sk[:32])
|
|
||||||
h.Sum(ek[:0])
|
|
||||||
|
|
||||||
sk.ck = clamp(ek[:32])
|
|
||||||
}
|
|
||||||
|
|
||||||
return sk.ck
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serialize the private key to file 'fn' using human readable
|
|
||||||
// 'comment' and encrypt the key with supplied passphrase 'pw'.
|
|
||||||
func (sk *PrivateKey) Serialize(fn, comment string, ovwrite bool, pw []byte) error {
|
|
||||||
b, err := sk.MarshalBinary(comment, pw)
|
|
||||||
if err == nil {
|
|
||||||
return writeFile(fn, b, ovwrite, 0600)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalBinary marshals the private key with a caller provided
|
|
||||||
// passphrase 'pw' and human readable 'comment'
|
|
||||||
func (sk *PrivateKey) MarshalBinary(comment string, pw []byte) ([]byte, error) {
|
|
||||||
// expand the password into 64 bytes
|
|
||||||
pass := sha512.Sum512(pw)
|
|
||||||
salt := make([]byte, 32)
|
|
||||||
|
|
||||||
randRead(salt)
|
|
||||||
|
|
||||||
// "32" == Length of AES-256 key
|
|
||||||
key, err := scrypt.Key(pass[:], salt, _N, _r, _p, 32)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("marshal: can't derive scrypt key: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
aes, err := aes.NewCipher(key)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("marshal: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ae, err := cipher.NewGCM(aes)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("marshal: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
tl := ae.Overhead()
|
|
||||||
buf := make([]byte, tl+len(sk.Sk))
|
|
||||||
esk := ae.Seal(buf[:0], salt[:ae.NonceSize()], sk.Sk, nil)
|
|
||||||
|
|
||||||
enc := base64.StdEncoding.EncodeToString
|
|
||||||
|
|
||||||
ssk := serializedPrivKey{
|
|
||||||
Comment: comment,
|
|
||||||
Esk: enc(esk),
|
|
||||||
Salt: enc(salt),
|
|
||||||
Algo: sk_algo,
|
|
||||||
N: _N,
|
|
||||||
R: _r,
|
|
||||||
P: _p,
|
|
||||||
}
|
|
||||||
|
|
||||||
// We won't protect the Scrypt parameters with the hash above
|
|
||||||
// because it is not needed. If the parameters are wrong, the
|
|
||||||
// derived key will be wrong and thus, the hash will not match.
|
|
||||||
|
|
||||||
return yaml.Marshal(&ssk)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalBinary unmarshals the private key and optionally invokes the
|
|
||||||
// caller provided getpw() function to read the password if needed. If the
|
|
||||||
// input byte stream 'b' is an OpenSSH ed25519 key, this function transparently
|
|
||||||
// decodes it.
|
|
||||||
func (sk *PrivateKey) UnmarshalBinary(b []byte, getpw func() ([]byte, error)) error {
|
|
||||||
if bytes.Index(b, []byte("OPENSSH PRIVATE KEY-")) > 0 {
|
|
||||||
xk, err := parseSSHPrivateKey(b, getpw)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*sk = *xk
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var pw []byte
|
|
||||||
if getpw != nil {
|
|
||||||
var err error
|
|
||||||
pw, err = getpw()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We take short passwords and extend them
|
|
||||||
pwb := sha512.Sum512(pw)
|
|
||||||
|
|
||||||
var ssk serializedPrivKey
|
|
||||||
|
|
||||||
err := yaml.Unmarshal(b, &ssk)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unmarshal priv key: can't parse YAML: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(ssk.Salt) == 0 || len(ssk.Esk) == 0 {
|
|
||||||
return fmt.Errorf("unmarshal priv key: not YAML format")
|
|
||||||
}
|
|
||||||
|
|
||||||
b64 := base64.StdEncoding.DecodeString
|
|
||||||
|
|
||||||
salt, err := b64(ssk.Salt)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unmarshal priv key: can't decode salt: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
esk, err := b64(ssk.Esk)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unmarshal priv key: can't decode key: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// "32" == Length of AES-256 key
|
|
||||||
key, err := scrypt.Key(pwb[:], salt, ssk.N, ssk.R, ssk.P, 32)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unmarshal priv key: can't derive key: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
aes, err := aes.NewCipher(key)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unmarshal priv key: aes failure: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ae, err := cipher.NewGCM(aes)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unmarshal priv key: aes failure: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
skb := make([]byte, 64)
|
|
||||||
skb, err = ae.Open(skb[:0], salt[:ae.NonceSize()], esk, nil)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unmarshal priv key: wrong password")
|
|
||||||
}
|
|
||||||
|
|
||||||
return makePrivateKeyFromBytes(sk, skb)
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Public Key Methods ---
|
|
||||||
|
|
||||||
// Read the public key from 'fn' and create new instance of
|
|
||||||
// PublicKey
|
|
||||||
func ReadPublicKey(fn string) (*PublicKey, error) {
|
|
||||||
var err error
|
|
||||||
var yml []byte
|
|
||||||
|
|
||||||
if yml, err = ioutil.ReadFile(fn); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var pk PublicKey
|
|
||||||
if err = pk.UnmarshalBinary(yml); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &pk, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse a serialized public in 'yml' and return the resulting
|
|
||||||
// public key instance
|
|
||||||
func MakePublicKey(yml []byte) (*PublicKey, error) {
|
|
||||||
var pk PublicKey
|
|
||||||
if err := pk.UnmarshalBinary(yml); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &pk, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make a public key from a string
|
|
||||||
func MakePublicKeyFromString(s string) (*PublicKey, error) {
|
|
||||||
// first try to decode it as a openssh key
|
|
||||||
if pk2, err := parseEncPubKey([]byte(s), "command-line-pk"); err == nil {
|
|
||||||
return pk2, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now try to decode as an sigtool key
|
|
||||||
b64 := base64.StdEncoding.DecodeString
|
|
||||||
|
|
||||||
pkb, err := b64(s)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var pk PublicKey
|
|
||||||
err = makePublicKeyFromBytes(&pk, pkb)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &pk, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func makePublicKeyFromBytes(pk *PublicKey, b []byte) error {
|
|
||||||
if len(b) != 32 {
|
|
||||||
return fmt.Errorf("public key is malformed (len %d!)", len(b))
|
|
||||||
}
|
|
||||||
|
|
||||||
pk.Pk = make([]byte, 32)
|
|
||||||
pk.hash = pkhash(b)
|
|
||||||
copy(pk.Pk, b)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make a public key from a byte string
|
|
||||||
func PublicKeyFromBytes(b []byte) (*PublicKey, error) {
|
|
||||||
var pk PublicKey
|
|
||||||
if err := makePublicKeyFromBytes(&pk, b); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &pk, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serialize a PublicKey into file 'fn' with a human readable 'comment'.
|
|
||||||
// If 'ovwrite' is true, overwrite the file if it exists.
|
|
||||||
func (pk *PublicKey) Serialize(fn, comment string, ovwrite bool) error {
|
|
||||||
out, err := pk.MarshalBinary(comment)
|
|
||||||
if err == nil {
|
|
||||||
return writeFile(fn, out, ovwrite, 0644)
|
|
||||||
}
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// from github.com/FiloSottile/age
|
|
||||||
var curve25519P, _ = new(big.Int).SetString("57896044618658097711785492504343953926634992332820282019728792003956564819949", 10)
|
|
||||||
|
|
||||||
// Convert an Ed25519 Public Key to Curve25519 public key
|
|
||||||
// from github.com/FiloSottile/age
|
|
||||||
func (pk *PublicKey) ToCurve25519PK() []byte {
|
|
||||||
if pk.ck != nil {
|
|
||||||
return pk.ck
|
|
||||||
}
|
|
||||||
|
|
||||||
// ed25519.PublicKey is a little endian representation of the y-coordinate,
|
|
||||||
// with the most significant bit set based on the sign of the x-ccordinate.
|
|
||||||
bigEndianY := make([]byte, Ed.PublicKeySize)
|
|
||||||
for i, b := range pk.Pk {
|
|
||||||
bigEndianY[Ed.PublicKeySize-i-1] = b
|
|
||||||
}
|
|
||||||
bigEndianY[0] &= 0b0111_1111
|
|
||||||
|
|
||||||
// The Montgomery u-coordinate is derived through the bilinear map
|
|
||||||
//
|
|
||||||
// u = (1 + y) / (1 - y)
|
|
||||||
//
|
|
||||||
// See https://blog.filippo.io/using-ed25519-keys-for-encryption.
|
|
||||||
y := new(big.Int).SetBytes(bigEndianY)
|
|
||||||
denom := big.NewInt(1)
|
|
||||||
denom.ModInverse(denom.Sub(denom, y), curve25519P) // 1 / (1 - y)
|
|
||||||
u := y.Mul(y.Add(y, big.NewInt(1)), denom)
|
|
||||||
u.Mod(u, curve25519P)
|
|
||||||
|
|
||||||
out := make([]byte, 32)
|
|
||||||
uBytes := u.Bytes()
|
|
||||||
n := len(uBytes)
|
|
||||||
for i, b := range uBytes {
|
|
||||||
out[n-i-1] = b
|
|
||||||
}
|
|
||||||
|
|
||||||
pk.ck = out
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// Public Key Hash
|
|
||||||
func (pk *PublicKey) Hash() []byte {
|
|
||||||
return pk.hash
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalBinary marshals a PublicKey into a byte array
|
|
||||||
func (pk *PublicKey) MarshalBinary(comment string) ([]byte, error) {
|
|
||||||
b64 := base64.StdEncoding.EncodeToString
|
|
||||||
spk := &serializedPubKey{
|
|
||||||
Comment: comment,
|
|
||||||
Pk: b64(pk.Pk),
|
|
||||||
Hash: b64(pk.hash),
|
|
||||||
}
|
|
||||||
|
|
||||||
return yaml.Marshal(spk)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalBinary constructs a PublicKey from a previously
|
|
||||||
// marshaled byte stream instance. In addition, it is also
|
|
||||||
// capable of parsing an OpenSSH ed25519 public key.
|
|
||||||
func (pk *PublicKey) UnmarshalBinary(yml []byte) error {
|
|
||||||
|
|
||||||
// first try to parse as a ssh key
|
|
||||||
if xk, err := parseSSHPublicKey(yml); err == nil {
|
|
||||||
*pk = *xk
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// OK Yaml it is.
|
|
||||||
|
|
||||||
var spk serializedPubKey
|
|
||||||
var err error
|
|
||||||
|
|
||||||
if err = yaml.Unmarshal(yml, &spk); err != nil {
|
|
||||||
return fmt.Errorf("can't parse YAML: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(spk.Pk) == 0 {
|
|
||||||
return fmt.Errorf("sign: not a YAML public key")
|
|
||||||
}
|
|
||||||
|
|
||||||
b64 := base64.StdEncoding.DecodeString
|
|
||||||
var pkb []byte
|
|
||||||
|
|
||||||
if pkb, err = b64(spk.Pk); err != nil {
|
|
||||||
return fmt.Errorf("can't decode YAML:Pk: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return makePublicKeyFromBytes(pk, pkb)
|
|
||||||
}
|
|
||||||
|
|
||||||
// -- Internal Utility Functions --
|
|
||||||
|
|
||||||
func clamp(k []byte) []byte {
|
|
||||||
k[0] &= 248
|
|
||||||
k[31] &= 127
|
|
||||||
k[31] |= 64
|
|
||||||
return k
|
|
||||||
}
|
|
||||||
|
|
||||||
// EOF
|
|
||||||
// vim: noexpandtab:ts=8:sw=8:tw=92:
|
|
45
sign/rand.go
45
sign/rand.go
|
@ -1,45 +0,0 @@
|
||||||
// rand.go - utility functions to generate random quantities
|
|
||||||
//
|
|
||||||
// (c) 2018 Sudhi Herle <sudhi@herle.net>
|
|
||||||
//
|
|
||||||
// Licensing Terms: GPLv2
|
|
||||||
//
|
|
||||||
// If you need a commercial license for this work, please contact
|
|
||||||
// the author.
|
|
||||||
//
|
|
||||||
// This software does not come with any express or implied
|
|
||||||
// warranty; it is provided "as is". No claim is made to its
|
|
||||||
// suitability for any purpose.
|
|
||||||
|
|
||||||
package sign
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/rand"
|
|
||||||
"encoding/binary"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
func randu32() uint32 {
|
|
||||||
var b [4]byte
|
|
||||||
|
|
||||||
_, err := io.ReadFull(rand.Reader, b[:])
|
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Sprintf("can't read 4 rand bytes: %s", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
return binary.LittleEndian.Uint32(b[:])
|
|
||||||
}
|
|
||||||
|
|
||||||
func randRead(b []byte) []byte {
|
|
||||||
_, err := io.ReadFull(rand.Reader, b)
|
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Sprintf("can't read %d bytes of random data: %s", len(b), err))
|
|
||||||
}
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
func randBuf(sz int) []byte {
|
|
||||||
b := make([]byte, sz)
|
|
||||||
return randRead(b)
|
|
||||||
}
|
|
535
sign/sign.go
535
sign/sign.go
|
@ -11,44 +11,358 @@
|
||||||
// warranty; it is provided "as is". No claim is made to its
|
// warranty; it is provided "as is". No claim is made to its
|
||||||
// suitability for any purpose.
|
// suitability for any purpose.
|
||||||
|
|
||||||
// This file implements:
|
// Package sign implements Ed25519 signing, verification on files.
|
||||||
// - key generation, and key I/O
|
// It builds upon golang.org/x/crypto/ed25519 by adding methods
|
||||||
// - sign/verify of files and byte strings
|
// for serializing and deserializing Ed25519 private & public keys.
|
||||||
|
// In addition, it works with large files - by precalculating their
|
||||||
|
// SHA512 checksum in mmap'd mode and sending the 64 byte signature
|
||||||
|
// for Ed25519 signing.
|
||||||
package sign
|
package sign
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"crypto"
|
"crypto"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
|
"crypto/sha256"
|
||||||
"crypto/sha512"
|
"crypto/sha512"
|
||||||
"crypto/subtle"
|
"crypto/subtle"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"hash"
|
||||||
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
|
||||||
Ed "crypto/ed25519"
|
Ed "crypto/ed25519"
|
||||||
|
"golang.org/x/crypto/scrypt"
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
|
|
||||||
"gopkg.in/yaml.v3"
|
"github.com/opencoff/go-utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Private Ed25519 key
|
||||||
|
type PrivateKey struct {
|
||||||
|
Sk []byte
|
||||||
|
|
||||||
|
// Encryption key: Curve25519 point corresponding to this Ed25519 key
|
||||||
|
ck []byte
|
||||||
|
|
||||||
|
// Cached copy of the public key
|
||||||
|
pk *PublicKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// Public Ed25519 key
|
||||||
|
type PublicKey struct {
|
||||||
|
Pk []byte
|
||||||
|
|
||||||
|
// Comment string
|
||||||
|
Comment string
|
||||||
|
|
||||||
|
// Curve25519 point corresponding to this Ed25519 key
|
||||||
|
ck []byte
|
||||||
|
|
||||||
|
hash []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ed25519 key pair
|
||||||
|
type Keypair struct {
|
||||||
|
Sec PrivateKey
|
||||||
|
Pub PublicKey
|
||||||
|
}
|
||||||
|
|
||||||
// An Ed25519 Signature
|
// An Ed25519 Signature
|
||||||
type Signature struct {
|
type Signature struct {
|
||||||
Sig []byte // Ed25519 sig bytes
|
Sig []byte // Ed25519 sig bytes
|
||||||
pkhash []byte // [0:16] SHA256 hash of public key needed for verification
|
pkhash []byte // [0:16] SHA256 hash of public key needed for verification
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Algorithm used in the encrypted private key
|
||||||
|
const sk_algo = "scrypt-sha256"
|
||||||
|
const sig_algo = "sha512-ed25519"
|
||||||
|
|
||||||
|
// Length of Ed25519 Public Key Hash
|
||||||
|
const PKHashLength = 16
|
||||||
|
|
||||||
|
// Scrypt parameters
|
||||||
|
const _N = 1 << 17
|
||||||
|
const _r = 16
|
||||||
|
const _p = 1
|
||||||
|
|
||||||
|
// Encrypted Private key
|
||||||
|
type encPrivKey struct {
|
||||||
|
// Encrypted Sk
|
||||||
|
Esk []byte
|
||||||
|
|
||||||
|
// parameters for Sk serialization
|
||||||
|
Salt []byte
|
||||||
|
|
||||||
|
// Algorithm used for checksum and KDF
|
||||||
|
Algo string
|
||||||
|
|
||||||
|
// Checksum to verify passphrase before we xor it
|
||||||
|
Verify []byte
|
||||||
|
|
||||||
|
// These are params for scrypt.Key()
|
||||||
|
// CPU Cost parameter; must be a power of 2
|
||||||
|
N uint32
|
||||||
|
// r * p should be less than 2^30
|
||||||
|
r uint32
|
||||||
|
p uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialized representation of private key
|
||||||
|
type serializedPrivKey struct {
|
||||||
|
Comment string `yaml:"comment,omitempty"`
|
||||||
|
Esk string `yaml:"esk"`
|
||||||
|
Salt string `yaml:"salt,omitempty"`
|
||||||
|
Algo string `yaml:"algo,omitempty"`
|
||||||
|
Verify string `yaml:"verify,omitempty"`
|
||||||
|
N uint32 `yaml:"Z,flow,omitempty"`
|
||||||
|
R uint32 `yaml:"r,flow,omitempty"`
|
||||||
|
P uint32 `yaml:"p,flow,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// serialized representation of public key
|
||||||
|
type serializedPubKey struct {
|
||||||
|
Comment string `yaml:"comment,omitempty"`
|
||||||
|
Pk string `yaml:"pk"`
|
||||||
|
Hash string `yaml:"hash"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialized signature
|
||||||
|
type signature struct {
|
||||||
|
Comment string `yaml:"comment,omitempty"`
|
||||||
|
Pkhash string `yaml:"pkhash,omitempty"`
|
||||||
|
Signature string `yaml:"signature"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func pkhash(pk []byte) []byte {
|
||||||
|
z := sha256.Sum256(pk)
|
||||||
|
return z[:PKHashLength]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate a new Ed25519 keypair
|
||||||
|
func NewKeypair() (*Keypair, error) {
|
||||||
|
//kp := &Keypair{Sec: PrivateKey{N: 1 << 17, r: 64, p: 1}}
|
||||||
|
kp := &Keypair{}
|
||||||
|
sk := &kp.Sec
|
||||||
|
pk := &kp.Pub
|
||||||
|
sk.pk = pk
|
||||||
|
|
||||||
|
p, s, err := Ed.GenerateKey(rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Can't generate Ed25519 keys: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pk.Pk = []byte(p)
|
||||||
|
sk.Sk = []byte(s)
|
||||||
|
pk.hash = pkhash(pk.Pk)
|
||||||
|
|
||||||
|
return kp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialize the keypair to two separate files. The basename of the
|
||||||
|
// file is 'bn'; the public key goes in $bn.pub and the private key
|
||||||
|
// goes in $bn.key.
|
||||||
|
// If password is non-empty, then the private key is encrypted
|
||||||
|
// before writing to disk.
|
||||||
|
func (kp *Keypair) Serialize(bn, comment string, getpw func() ([]byte, error)) error {
|
||||||
|
|
||||||
|
sk := &kp.Sec
|
||||||
|
pk := &kp.Pub
|
||||||
|
|
||||||
|
skf := fmt.Sprintf("%s.key", bn)
|
||||||
|
pkf := fmt.Sprintf("%s.pub", bn)
|
||||||
|
|
||||||
|
err := pk.serialize(pkf, comment)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Can't serialize to %s: %s", pkf, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = sk.serialize(skf, comment, getpw)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Can't serialize to %s: %s", pkf, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the private key in 'fn', optionally decrypting it using
|
||||||
|
// password 'pw' and create new instance of PrivateKey
|
||||||
|
func ReadPrivateKey(fn string, getpw func() ([]byte, error)) (*PrivateKey, error) {
|
||||||
|
yml, err := ioutil.ReadFile(fn)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if bytes.Index(yml, []byte("OPENSSH PRIVATE KEY-")) > 0 {
|
||||||
|
return parseSSHPrivateKey(yml, getpw)
|
||||||
|
}
|
||||||
|
|
||||||
|
if pw, err := getpw(); err == nil {
|
||||||
|
return MakePrivateKey(yml, pw)
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make a private key from bytes 'yml' and password 'pw'. The bytes
|
||||||
|
// are assumed to be serialized version of the private key.
|
||||||
|
func MakePrivateKey(yml []byte, pw []byte) (*PrivateKey, error) {
|
||||||
|
var ssk serializedPrivKey
|
||||||
|
|
||||||
|
err := yaml.Unmarshal(yml, &ssk)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("can't parse YAML: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
esk := &encPrivKey{N: ssk.N, r: ssk.R, p: ssk.P, Algo: ssk.Algo}
|
||||||
|
b64 := base64.StdEncoding.DecodeString
|
||||||
|
|
||||||
|
esk.Esk, err = b64(ssk.Esk)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("can't decode YAML:Esk: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
esk.Salt, err = b64(ssk.Salt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("can't decode YAML:Salt: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
esk.Verify, err = b64(ssk.Verify)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("can't decode YAML:Verify: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We take short passwords and extend them
|
||||||
|
pwb := sha512.Sum512(pw)
|
||||||
|
|
||||||
|
xork, err := scrypt.Key(pwb[:], esk.Salt, int(esk.N), int(esk.r), int(esk.p), len(esk.Esk))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("can't derive key: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
hh := sha256.New()
|
||||||
|
hh.Write(esk.Salt)
|
||||||
|
hh.Write(xork)
|
||||||
|
ck := hh.Sum(nil)
|
||||||
|
|
||||||
|
if subtle.ConstantTimeCompare(esk.Verify, ck) != 1 {
|
||||||
|
return nil, fmt.Errorf("incorrect private key password")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Everything works. Now, decode the key
|
||||||
|
skb := make([]byte, len(esk.Esk))
|
||||||
|
for i := 0; i < len(esk.Esk); i++ {
|
||||||
|
skb[i] = esk.Esk[i] ^ xork[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
return PrivateKeyFromBytes(skb)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make a private key from 64-bytes of extended Ed25519 key
|
||||||
|
func PrivateKeyFromBytes(buf []byte) (*PrivateKey, error) {
|
||||||
|
if len(buf) != 64 {
|
||||||
|
return nil, fmt.Errorf("private key is malformed (len %d!)", len(buf))
|
||||||
|
}
|
||||||
|
|
||||||
|
skb := make([]byte, 64)
|
||||||
|
copy(skb, buf)
|
||||||
|
|
||||||
|
edsk := Ed.PrivateKey(skb)
|
||||||
|
edpk := edsk.Public().(Ed.PublicKey)
|
||||||
|
|
||||||
|
pk := &PublicKey{
|
||||||
|
Pk: []byte(edpk),
|
||||||
|
hash: pkhash([]byte(edpk)),
|
||||||
|
}
|
||||||
|
sk := &PrivateKey{
|
||||||
|
Sk: skb,
|
||||||
|
pk: pk,
|
||||||
|
}
|
||||||
|
|
||||||
|
return sk, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given a secret key, return the corresponding Public Key
|
||||||
|
func (sk *PrivateKey) PublicKey() *PublicKey {
|
||||||
|
return sk.pk
|
||||||
|
}
|
||||||
|
|
||||||
|
// Public Key Hash
|
||||||
|
func (pk *PublicKey) Hash() []byte {
|
||||||
|
return pk.hash
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialize the private key to a file
|
||||||
|
// Format: YAML
|
||||||
|
// All []byte are in base64 (RawEncoding)
|
||||||
|
func (sk *PrivateKey) serialize(fn, comment string, getpw func() ([]byte, error)) error {
|
||||||
|
|
||||||
|
pw, err := getpw()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
b64 := base64.StdEncoding.EncodeToString
|
||||||
|
esk := &encPrivKey{}
|
||||||
|
ssk := &serializedPrivKey{Comment: comment}
|
||||||
|
|
||||||
|
// expand the password into 64 bytes
|
||||||
|
pwb := sha512.Sum512(pw)
|
||||||
|
|
||||||
|
esk.N = _N
|
||||||
|
esk.r = _r
|
||||||
|
esk.p = _p
|
||||||
|
|
||||||
|
esk.Salt = make([]byte, 32)
|
||||||
|
esk.Esk = make([]byte, len(sk.Sk))
|
||||||
|
|
||||||
|
randread(esk.Salt)
|
||||||
|
xork, err := scrypt.Key(pwb[:], esk.Salt, int(esk.N), int(esk.r), int(esk.p), len(sk.Sk))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Can't derive scrypt key: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
hh := sha256.New()
|
||||||
|
hh.Write(esk.Salt)
|
||||||
|
hh.Write(xork)
|
||||||
|
esk.Verify = hh.Sum(nil)
|
||||||
|
|
||||||
|
// We won't protect the Scrypt parameters with the hash above
|
||||||
|
// because it is not needed. If the parameters are wrong, the
|
||||||
|
// derived key will be wrong and thus, the hash will not match.
|
||||||
|
|
||||||
|
esk.Algo = sk_algo // global var
|
||||||
|
|
||||||
|
// Finally setup the encrypted key
|
||||||
|
for i := 0; i < len(sk.Sk); i++ {
|
||||||
|
esk.Esk[i] = sk.Sk[i] ^ xork[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
ssk.Esk = b64(esk.Esk)
|
||||||
|
ssk.Salt = b64(esk.Salt)
|
||||||
|
ssk.Verify = b64(esk.Verify)
|
||||||
|
ssk.Algo = esk.Algo
|
||||||
|
ssk.N = esk.N
|
||||||
|
ssk.R = esk.r
|
||||||
|
ssk.P = esk.p
|
||||||
|
|
||||||
|
out, err := yaml.Marshal(ssk)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("can't marahal to YAML: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return writeFile(fn, out, 0600)
|
||||||
|
}
|
||||||
|
|
||||||
// Sign a prehashed Message; return the signature as opaque bytes
|
// Sign a prehashed Message; return the signature as opaque bytes
|
||||||
// Signature is an YAML file:
|
// Signature is an YAML file:
|
||||||
//
|
// Comment: source file path
|
||||||
// Comment: source file path
|
// Signature: Ed25519 signature
|
||||||
// Signature: Ed25519 signature
|
|
||||||
func (sk *PrivateKey) SignMessage(ck []byte, comment string) (*Signature, error) {
|
func (sk *PrivateKey) SignMessage(ck []byte, comment string) (*Signature, error) {
|
||||||
h := sha512.New()
|
|
||||||
h.Write([]byte("sigtool signed message"))
|
|
||||||
h.Write(ck)
|
|
||||||
ck = h.Sum(nil)[:]
|
|
||||||
|
|
||||||
x := Ed.PrivateKey(sk.Sk)
|
x := Ed.PrivateKey(sk.Sk)
|
||||||
|
|
||||||
sig, err := x.Sign(rand.Reader, ck, crypto.Hash(0))
|
sig, err := x.Sign(rand.Reader, ck, crypto.Hash(0))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("can't sign %x: %s", ck, err)
|
return nil, fmt.Errorf("can't sign %x: %s", ck, err)
|
||||||
|
@ -88,13 +402,12 @@ func ReadSignature(fn string) (*Signature, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var sig Signature
|
return MakeSignature(yml)
|
||||||
return makeSignature(&sig, yml)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse serialized signature from bytes 'b' and construct a
|
// Parse serialized signature from bytes 'b' and construct a
|
||||||
// Signature object
|
// Signature object
|
||||||
func makeSignature(sig *Signature, b []byte) (*Signature, error) {
|
func MakeSignature(b []byte) (*Signature, error) {
|
||||||
var ss signature
|
var ss signature
|
||||||
err := yaml.Unmarshal(b, &ss)
|
err := yaml.Unmarshal(b, &ss)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -113,33 +426,29 @@ func makeSignature(sig *Signature, b []byte) (*Signature, error) {
|
||||||
return nil, fmt.Errorf("can't decode Base64:Pkhash <%s>: %s", ss.Pkhash, err)
|
return nil, fmt.Errorf("can't decode Base64:Pkhash <%s>: %s", ss.Pkhash, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sig.Sig = s
|
return &Signature{Sig: s, pkhash: p}, nil
|
||||||
sig.pkhash = p
|
|
||||||
return sig, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarshalBinary marshals a signature into a byte stream with
|
// Serialize a signature suitable for storing in durable media
|
||||||
// an optional caller supplied comment.
|
func (sig *Signature) Serialize(comment string) ([]byte, error) {
|
||||||
func (sig *Signature) MarshalBinary(comment string) ([]byte, error) {
|
|
||||||
sigs := base64.StdEncoding.EncodeToString(sig.Sig)
|
sigs := base64.StdEncoding.EncodeToString(sig.Sig)
|
||||||
pks := base64.StdEncoding.EncodeToString(sig.pkhash)
|
pks := base64.StdEncoding.EncodeToString(sig.pkhash)
|
||||||
ss := &signature{Comment: comment, Pkhash: pks, Signature: sigs}
|
ss := &signature{Comment: comment, Pkhash: pks, Signature: sigs}
|
||||||
|
|
||||||
return yaml.Marshal(ss)
|
out, err := yaml.Marshal(ss)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("can't marshal signature of %x to YAML: %s", sig.Sig, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalBinary constructs a Signature from a previously
|
// SerializeFile serializes the signature to an output file 'f'
|
||||||
// serialized bytestream
|
func (sig *Signature) SerializeFile(fn, comment string) error {
|
||||||
func (sig *Signature) UnmarshalBinary(b []byte) error {
|
b, err := sig.Serialize(comment)
|
||||||
_, err := makeSignature(sig, b)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serialize a signature suitable for storing in durable media
|
|
||||||
func (sig *Signature) Serialize(fn, comment string, ovwrite bool) error {
|
|
||||||
b, err := sig.MarshalBinary(comment)
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = writeFile(fn, b, ovwrite, 0644)
|
err = writeFile(fn, b, 0644)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -151,27 +460,171 @@ func (sig *Signature) IsPKMatch(pk *PublicKey) bool {
|
||||||
return subtle.ConstantTimeCompare(pk.hash, sig.pkhash) == 1
|
return subtle.ConstantTimeCompare(pk.hash, sig.pkhash) == 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Public Key Methods ---
|
||||||
|
|
||||||
|
// Read the public key from 'fn' and create new instance of
|
||||||
|
// PublicKey
|
||||||
|
func ReadPublicKey(fn string) (*PublicKey, error) {
|
||||||
|
var err error
|
||||||
|
var yml []byte
|
||||||
|
|
||||||
|
if yml, err = ioutil.ReadFile(fn); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// first try to parse as a ssh key
|
||||||
|
pk, err := parseSSHPublicKey(yml)
|
||||||
|
if err != nil {
|
||||||
|
pk, err = MakePublicKey(yml)
|
||||||
|
}
|
||||||
|
return pk, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse a serialized public in 'yml' and return the resulting
|
||||||
|
// public key instance
|
||||||
|
func MakePublicKey(yml []byte) (*PublicKey, error) {
|
||||||
|
var spk serializedPubKey
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if err = yaml.Unmarshal(yml, &spk); err != nil {
|
||||||
|
return nil, fmt.Errorf("can't parse YAML: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
b64 := base64.StdEncoding.DecodeString
|
||||||
|
var pkb []byte
|
||||||
|
|
||||||
|
if pkb, err = b64(spk.Pk); err != nil {
|
||||||
|
return nil, fmt.Errorf("can't decode YAML:Pk: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if pk, err := PublicKeyFromBytes(pkb); err == nil {
|
||||||
|
pk.Comment = spk.Comment
|
||||||
|
return pk, nil
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make a public key from a byte string
|
||||||
|
func PublicKeyFromBytes(b []byte) (*PublicKey, error) {
|
||||||
|
if len(b) != 32 {
|
||||||
|
return nil, fmt.Errorf("public key is malformed (len %d!)", len(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
pk := &PublicKey{
|
||||||
|
Pk: make([]byte, 32),
|
||||||
|
hash: pkhash(b),
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(pk.Pk, b)
|
||||||
|
return pk, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialize Public Keys
|
||||||
|
func (pk *PublicKey) serialize(fn, comment string) error {
|
||||||
|
b64 := base64.StdEncoding.EncodeToString
|
||||||
|
spk := &serializedPubKey{
|
||||||
|
Comment: comment,
|
||||||
|
Pk: b64(pk.Pk),
|
||||||
|
Hash: b64(pk.hash),
|
||||||
|
}
|
||||||
|
|
||||||
|
out, err := yaml.Marshal(spk)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("can't marahal to YAML: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return writeFile(fn, out, 0644)
|
||||||
|
}
|
||||||
|
|
||||||
// Verify a signature 'sig' for file 'fn' against public key 'pk'
|
// Verify a signature 'sig' for file 'fn' against public key 'pk'
|
||||||
// Return True if signature matches, False otherwise
|
// Return True if signature matches, False otherwise
|
||||||
func (pk *PublicKey) VerifyFile(fn string, sig *Signature) (bool, error) {
|
func (pk *PublicKey) VerifyFile(fn string, sig *Signature) (bool, error) {
|
||||||
|
|
||||||
ck, err := fileCksum(fn, sha512.New())
|
ck, err := fileCksum(fn, sha512.New())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return pk.VerifyMessage(ck, sig), nil
|
return pk.VerifyMessage(ck, sig)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify a signature 'sig' for a pre-calculated checksum 'ck' against public key 'pk'
|
// Verify a signature 'sig' for a pre-calculated checksum 'ck' against public key 'pk'
|
||||||
// Return True if signature matches, False otherwise
|
// Return True if signature matches, False otherwise
|
||||||
func (pk *PublicKey) VerifyMessage(ck []byte, sig *Signature) bool {
|
func (pk *PublicKey) VerifyMessage(ck []byte, sig *Signature) (bool, error) {
|
||||||
h := sha512.New()
|
|
||||||
h.Write([]byte("sigtool signed message"))
|
|
||||||
h.Write(ck)
|
|
||||||
ck = h.Sum(nil)[:]
|
|
||||||
|
|
||||||
x := Ed.PublicKey(pk.Pk)
|
x := Ed.PublicKey(pk.Pk)
|
||||||
return Ed.Verify(x, ck, sig.Sig)
|
return Ed.Verify(x, ck, sig.Sig), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -- Internal Utility Functions --
|
||||||
|
|
||||||
|
// Unlink a file.
|
||||||
|
func unlink(f string) {
|
||||||
|
st, err := os.Stat(f)
|
||||||
|
if err == nil {
|
||||||
|
if !st.Mode().IsRegular() {
|
||||||
|
panic(fmt.Sprintf("%s can't be unlinked. Not a regular file?", f))
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Remove(f)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple function to reliably write data to a file.
|
||||||
|
// Does MORE than ioutil.WriteFile() - in that it doesn't trash the
|
||||||
|
// existing file with an incomplete write.
|
||||||
|
func writeFile(fn string, b []byte, mode uint32) error {
|
||||||
|
tmp := fmt.Sprintf("%s.tmp", fn)
|
||||||
|
unlink(tmp)
|
||||||
|
|
||||||
|
fd, err := os.OpenFile(tmp, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.FileMode(mode))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Can't create file %s: %s", tmp, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = fd.Write(b)
|
||||||
|
if err != nil {
|
||||||
|
fd.Close()
|
||||||
|
// XXX Do we delete the tmp file?
|
||||||
|
return fmt.Errorf("Can't write %v bytes to %s: %s", len(b), tmp, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fd.Close() // we ignore close(2) errors; unrecoverable anyway.
|
||||||
|
|
||||||
|
os.Rename(tmp, fn)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate file checksum out of hash function h
|
||||||
|
func fileCksum(fn string, h hash.Hash) ([]byte, error) {
|
||||||
|
|
||||||
|
fd, err := os.Open(fn)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("can't open %s: %s", fn, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer fd.Close()
|
||||||
|
|
||||||
|
sz, err := utils.MmapReader(fd, 0, 0, h)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var b [8]byte
|
||||||
|
binary.BigEndian.PutUint64(b[:], uint64(sz))
|
||||||
|
h.Write(b[:])
|
||||||
|
|
||||||
|
return h.Sum(nil), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func randread(b []byte) []byte {
|
||||||
|
_, err := io.ReadFull(rand.Reader, b)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("can't read %d bytes of random data: %s", len(b), err))
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// EOF
|
||||||
// vim: noexpandtab:ts=8:sw=8:tw=92:
|
// vim: noexpandtab:ts=8:sw=8:tw=92:
|
||||||
|
|
|
@ -14,13 +14,39 @@
|
||||||
package sign
|
package sign
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/subtle"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
|
// module under test
|
||||||
|
//"github.com/sign"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func newAsserter(t *testing.T) func(cond bool, msg string, args ...interface{}) {
|
||||||
|
return func(cond bool, msg string, args ...interface{}) {
|
||||||
|
if cond {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, file, line, ok := runtime.Caller(1)
|
||||||
|
if !ok {
|
||||||
|
file = "???"
|
||||||
|
line = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
s := fmt.Sprintf(msg, args...)
|
||||||
|
t.Fatalf("%s: %d: Assertion failed: %s\n", file, line, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return true if two byte arrays are equal
|
||||||
|
func byteEq(x, y []byte) bool {
|
||||||
|
return subtle.ConstantTimeCompare(x, y) == 1
|
||||||
|
}
|
||||||
|
|
||||||
// Return a temp dir in a temp-dir
|
// Return a temp dir in a temp-dir
|
||||||
func tempdir(t *testing.T) string {
|
func tempdir(t *testing.T) string {
|
||||||
assert := newAsserter(t)
|
assert := newAsserter(t)
|
||||||
|
@ -28,7 +54,7 @@ func tempdir(t *testing.T) string {
|
||||||
var b [10]byte
|
var b [10]byte
|
||||||
|
|
||||||
dn := os.TempDir()
|
dn := os.TempDir()
|
||||||
randRead(b[:])
|
randread(b[:])
|
||||||
|
|
||||||
tmp := path.Join(dn, fmt.Sprintf("%x", b[:]))
|
tmp := path.Join(dn, fmt.Sprintf("%x", b[:]))
|
||||||
err := os.MkdirAll(tmp, 0755)
|
err := os.MkdirAll(tmp, 0755)
|
||||||
|
@ -38,20 +64,16 @@ func tempdir(t *testing.T) string {
|
||||||
return tmp
|
return tmp
|
||||||
}
|
}
|
||||||
|
|
||||||
var fixedPw = []byte("abc")
|
|
||||||
var badPw = []byte("def")
|
|
||||||
var nilPw []byte
|
|
||||||
|
|
||||||
// return a hardcoded password
|
// return a hardcoded password
|
||||||
func hardcodedPw() ([]byte, error) {
|
func hardcodedPw() ([]byte, error) {
|
||||||
return fixedPw, nil
|
return []byte("abc"), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func wrongPw() ([]byte, error) {
|
func wrongPw() ([]byte, error) {
|
||||||
return badPw, nil
|
return []byte("xyz"), nil
|
||||||
}
|
}
|
||||||
func emptyPw() ([]byte, error) {
|
func emptyPw() ([]byte, error) {
|
||||||
return nilPw, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return true if file exists, false otherwise
|
// Return true if file exists, false otherwise
|
||||||
|
@ -81,109 +103,115 @@ p: 1
|
||||||
`
|
`
|
||||||
|
|
||||||
// #1. Create new key pair, and read them back.
|
// #1. Create new key pair, and read them back.
|
||||||
func TestSignSimple(t *testing.T) {
|
func Test0(t *testing.T) {
|
||||||
assert := newAsserter(t)
|
assert := newAsserter(t)
|
||||||
|
|
||||||
sk, err := NewPrivateKey()
|
kp, err := NewKeypair()
|
||||||
assert(err == nil, "NewPrivateKey() fail")
|
assert(err == nil, "NewKeyPair() fail")
|
||||||
|
|
||||||
pk := sk.PublicKey()
|
dn := tempdir(t)
|
||||||
|
|
||||||
dn := t.TempDir()
|
|
||||||
bn := fmt.Sprintf("%s/t0", dn)
|
bn := fmt.Sprintf("%s/t0", dn)
|
||||||
|
|
||||||
|
err = kp.Serialize(bn, "", hardcodedPw)
|
||||||
|
assert(err == nil, "keyPair.Serialize() fail")
|
||||||
|
|
||||||
pkf := fmt.Sprintf("%s.pub", bn)
|
pkf := fmt.Sprintf("%s.pub", bn)
|
||||||
skf := fmt.Sprintf("%s.key", bn)
|
skf := fmt.Sprintf("%s.key", bn)
|
||||||
|
|
||||||
err = pk.Serialize(pkf, "", true)
|
|
||||||
assert(err == nil, "can't serialize pk %s", pkf)
|
|
||||||
|
|
||||||
// try to overwrite
|
|
||||||
err = pk.Serialize(pkf, "", false)
|
|
||||||
assert(err != nil, "pk %s overwritten!", pkf)
|
|
||||||
|
|
||||||
err = sk.Serialize(skf, "", true, fixedPw)
|
|
||||||
assert(err == nil, "can't serialize sk %s", skf)
|
|
||||||
|
|
||||||
err = sk.Serialize(skf, "", false, nilPw)
|
|
||||||
assert(err != nil, "sk %s overwritten!", skf)
|
|
||||||
|
|
||||||
// We must find these two files
|
// We must find these two files
|
||||||
assert(fileExists(pkf), "missing pkf %s", pkf)
|
assert(fileExists(pkf), "missing pkf")
|
||||||
assert(fileExists(skf), "missing skf %s", skf)
|
assert(fileExists(skf), "missing skf")
|
||||||
|
|
||||||
npk, err := ReadPublicKey(pkf)
|
pk, err := ReadPublicKey(pkf)
|
||||||
assert(err == nil, "ReadPK() fail")
|
assert(err == nil, "ReadPK() fail")
|
||||||
|
|
||||||
// send the public key as private key
|
// -ditto- for Sk
|
||||||
nsk, err := ReadPrivateKey(pkf, emptyPw)
|
sk, err := ReadPrivateKey(pkf, emptyPw)
|
||||||
assert(err != nil, "bad SK ReadSK fail: %s", err)
|
assert(err != nil, "bad SK ReadSK fail: %s", err)
|
||||||
|
|
||||||
nsk, err = ReadPrivateKey(skf, emptyPw)
|
sk, err = ReadPrivateKey(skf, emptyPw)
|
||||||
assert(err != nil, "ReadSK() worked with empty pw")
|
assert(err != nil, "ReadSK() empty pw fail: ks", err)
|
||||||
|
|
||||||
nsk, err = ReadPrivateKey(skf, wrongPw)
|
sk, err = ReadPrivateKey(skf, wrongPw)
|
||||||
assert(err != nil, "ReadSK() worked with wrong pw")
|
assert(err != nil, "ReadSK() wrong pw fail: %s", err)
|
||||||
|
|
||||||
badf := fmt.Sprintf("%s/badf.key", dn)
|
badf := fmt.Sprintf("%s/badf.key", dn)
|
||||||
err = ioutil.WriteFile(badf, []byte(badsk), 0600)
|
err = ioutil.WriteFile(badf, []byte(badsk), 0600)
|
||||||
assert(err == nil, "can't write badsk: %s", err)
|
assert(err == nil, "write badsk")
|
||||||
|
|
||||||
nsk, err = ReadPrivateKey(badf, hardcodedPw)
|
sk, err = ReadPrivateKey(badf, hardcodedPw)
|
||||||
assert(err != nil, "decoded bad SK")
|
assert(err != nil, "badsk read fail: %s", err)
|
||||||
|
|
||||||
// Finally, with correct password it should work.
|
// Finally, with correct password it should work.
|
||||||
nsk, err = ReadPrivateKey(skf, hardcodedPw)
|
sk, err = ReadPrivateKey(skf, hardcodedPw)
|
||||||
assert(err == nil, "ReadSK() correct pw fail: %s", err)
|
assert(err == nil, "ReadSK() correct pw fail")
|
||||||
|
|
||||||
// And, deserialized keys should be identical
|
// And, deserialized keys should be identical
|
||||||
assert(byteEq(pk.Pk, npk.Pk), "pkbytes unequal")
|
assert(byteEq(pk.Pk, kp.Pub.Pk), "pkbytes unequal")
|
||||||
assert(byteEq(sk.Sk, nsk.Sk), "skbytes unequal")
|
assert(byteEq(sk.Sk, kp.Sec.Sk), "skbytes unequal")
|
||||||
|
|
||||||
|
os.RemoveAll(dn)
|
||||||
}
|
}
|
||||||
|
|
||||||
// #2. Create new key pair, sign a rand buffer and verify
|
// #2. Create new key pair, sign a rand buffer and verify
|
||||||
func TestSignRandBuf(t *testing.T) {
|
func Test1(t *testing.T) {
|
||||||
assert := newAsserter(t)
|
assert := newAsserter(t)
|
||||||
|
kp, err := NewKeypair()
|
||||||
sk, err := NewPrivateKey()
|
assert(err == nil, "NewKeyPair() fail")
|
||||||
assert(err == nil, "NewPrivateKey() fail: %s", err)
|
|
||||||
|
|
||||||
var ck [64]byte // simulates sha512 sum
|
var ck [64]byte // simulates sha512 sum
|
||||||
|
|
||||||
randRead(ck[:])
|
randread(ck[:])
|
||||||
|
|
||||||
pk := sk.PublicKey()
|
pk := &kp.Pub
|
||||||
|
sk := &kp.Sec
|
||||||
|
|
||||||
ss, err := sk.SignMessage(ck[:], "")
|
ss, err := sk.SignMessage(ck[:], "")
|
||||||
assert(err == nil, "sk.sign fail: %s", err)
|
assert(err == nil, "sk.sign fail")
|
||||||
assert(ss != nil, "sig is null")
|
assert(ss != nil, "sig is null")
|
||||||
|
|
||||||
// verify sig
|
// verify sig
|
||||||
assert(ss.IsPKMatch(pk), "pk match fail")
|
assert(ss.IsPKMatch(pk), "pk match fail")
|
||||||
|
|
||||||
// Corrupt the pkhash and see
|
// Corrupt the pkhash and see
|
||||||
randRead(ss.pkhash)
|
randread(ss.pkhash)
|
||||||
assert(!ss.IsPKMatch(pk), "corrupt pk match fail")
|
assert(!ss.IsPKMatch(pk), "corrupt pk match fail")
|
||||||
|
|
||||||
// Incorrect checksum == should fail verification
|
// Incorrect checksum == should fail verification
|
||||||
ok := pk.VerifyMessage(ck[:16], ss)
|
ok, err := pk.VerifyMessage(ck[:16], ss)
|
||||||
|
assert(err == nil, "bad ck verify err fail")
|
||||||
assert(!ok, "bad ck verify fail")
|
assert(!ok, "bad ck verify fail")
|
||||||
|
|
||||||
// proper checksum == should work
|
// proper checksum == should work
|
||||||
ok = pk.VerifyMessage(ck[:], ss)
|
ok, err = pk.VerifyMessage(ck[:], ss)
|
||||||
|
assert(err == nil, "verify err")
|
||||||
assert(ok, "verify fail")
|
assert(ok, "verify fail")
|
||||||
|
|
||||||
// Now sign a file
|
// Now sign a file
|
||||||
dn := t.TempDir()
|
dn := tempdir(t)
|
||||||
|
bn := fmt.Sprintf("%s/k", dn)
|
||||||
|
|
||||||
|
pkf := fmt.Sprintf("%s.pub", bn)
|
||||||
|
skf := fmt.Sprintf("%s.key", bn)
|
||||||
|
|
||||||
|
err = kp.Serialize(bn, "", emptyPw)
|
||||||
|
assert(err == nil, "keyPair.Serialize() fail")
|
||||||
|
|
||||||
|
// Now read the private key and sign
|
||||||
|
sk, err = ReadPrivateKey(skf, emptyPw)
|
||||||
|
assert(err == nil, "readSK fail")
|
||||||
|
|
||||||
|
pk, err = ReadPublicKey(pkf)
|
||||||
|
assert(err == nil, "ReadPK fail")
|
||||||
|
|
||||||
var buf [8192]byte
|
var buf [8192]byte
|
||||||
|
|
||||||
zf := fmt.Sprintf("%s/file.dat", dn)
|
zf := fmt.Sprintf("%s/file.dat", dn)
|
||||||
fd, err := os.OpenFile(zf, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
fd, err := os.OpenFile(zf, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||||
assert(err == nil, "file.dat creat file: %s", err)
|
assert(err == nil, "file.dat creat file")
|
||||||
|
|
||||||
for i := 0; i < 8; i++ {
|
for i := 0; i < 8; i++ {
|
||||||
randRead(buf[:])
|
randread(buf[:])
|
||||||
n, err := fd.Write(buf[:])
|
n, err := fd.Write(buf[:])
|
||||||
assert(err == nil, fmt.Sprintf("file.dat write fail: %s", err))
|
assert(err == nil, fmt.Sprintf("file.dat write fail: %s", err))
|
||||||
assert(n == 8192, fmt.Sprintf("file.dat i/o fail: exp 8192 saw %v", n))
|
assert(n == 8192, fmt.Sprintf("file.dat i/o fail: exp 8192 saw %v", n))
|
||||||
|
@ -192,31 +220,27 @@ func TestSignRandBuf(t *testing.T) {
|
||||||
fd.Close()
|
fd.Close()
|
||||||
|
|
||||||
sig, err := sk.SignFile(zf)
|
sig, err := sk.SignFile(zf)
|
||||||
assert(err == nil, "file.dat sign fail: %s", err)
|
assert(err == nil, "file.dat sign fail")
|
||||||
assert(sig != nil, "file.dat sign nil")
|
assert(sig != nil, "file.dat sign nil")
|
||||||
|
|
||||||
ok, err = pk.VerifyFile(zf, sig)
|
ok, err = pk.VerifyFile(zf, sig)
|
||||||
assert(err == nil, "file.dat verify fail: %s", err)
|
assert(err == nil, "file.dat verify fail")
|
||||||
assert(ok, "file.dat verify false")
|
assert(ok, "file.dat verify false")
|
||||||
|
|
||||||
// Now, serialize the signature and read it back
|
// Now, serialize the signature and read it back
|
||||||
sf := fmt.Sprintf("%s/file.sig", dn)
|
sf := fmt.Sprintf("%s/file.sig", dn)
|
||||||
err = sig.Serialize(sf, "", true)
|
err = sig.SerializeFile(sf, "")
|
||||||
assert(err == nil, "sig serialize fail: %s", err)
|
assert(err == nil, "sig serialize fail")
|
||||||
|
|
||||||
// now try to overwrite it
|
|
||||||
err = sig.Serialize(sf, "", false)
|
|
||||||
assert(err != nil, "sig serialize overwrote?!")
|
|
||||||
|
|
||||||
s2, err := ReadSignature(sf)
|
s2, err := ReadSignature(sf)
|
||||||
assert(err == nil, "file.sig read fail: %s", err)
|
assert(err == nil, "file.sig read fail")
|
||||||
assert(s2 != nil, "file.sig sig nil")
|
assert(s2 != nil, "file.sig sig nil")
|
||||||
|
|
||||||
assert(byteEq(s2.Sig, sig.Sig), "sig compare fail")
|
assert(byteEq(s2.Sig, sig.Sig), "sig compare fail")
|
||||||
|
|
||||||
// If we give a wrong file, verify must fail
|
// If we give a wrong file, verify must fail
|
||||||
st, err := os.Stat(zf)
|
st, err := os.Stat(zf)
|
||||||
assert(err == nil, "file.dat stat fail: %s", err)
|
assert(err == nil, "file.dat stat fail")
|
||||||
|
|
||||||
n := st.Size()
|
n := st.Size()
|
||||||
assert(n == 8192*8, "file.dat size fail")
|
assert(n == 8192*8, "file.dat size fail")
|
||||||
|
@ -224,12 +248,12 @@ func TestSignRandBuf(t *testing.T) {
|
||||||
os.Truncate(zf, n-1)
|
os.Truncate(zf, n-1)
|
||||||
|
|
||||||
st, err = os.Stat(zf)
|
st, err = os.Stat(zf)
|
||||||
assert(err == nil, "file.dat stat2 fail: %s", err)
|
assert(err == nil, "file.dat stat2 fail")
|
||||||
assert(st.Size() == (n-1), "truncate fail")
|
assert(st.Size() == (n-1), "truncate fail")
|
||||||
|
|
||||||
// Now verify this corrupt file
|
// Now verify this corrupt file
|
||||||
ok, err = pk.VerifyFile(zf, sig)
|
ok, err = pk.VerifyFile(zf, sig)
|
||||||
assert(err == nil, "file.dat corrupt i/o fail: %s", err)
|
assert(err == nil, "file.dat corrupt i/o fail")
|
||||||
assert(!ok, "file.dat corrupt verify false")
|
assert(!ok, "file.dat corrupt verify false")
|
||||||
|
|
||||||
os.RemoveAll(dn)
|
os.RemoveAll(dn)
|
||||||
|
@ -237,7 +261,7 @@ func TestSignRandBuf(t *testing.T) {
|
||||||
|
|
||||||
func Benchmark_Keygen(b *testing.B) {
|
func Benchmark_Keygen(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
_, _ = NewPrivateKey()
|
_, _ = NewKeypair()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -248,14 +272,13 @@ func Benchmark_Sig(b *testing.B) {
|
||||||
64,
|
64,
|
||||||
1024,
|
1024,
|
||||||
4096,
|
4096,
|
||||||
256 * 1024,
|
256*1024,
|
||||||
1048576,
|
1048576,
|
||||||
4 * 1048576,
|
4 * 1048576,
|
||||||
}
|
}
|
||||||
|
|
||||||
b.StopTimer()
|
b.StopTimer()
|
||||||
sk, _ := NewPrivateKey()
|
kp, _ := NewKeypair()
|
||||||
pk := sk.PublicKey()
|
|
||||||
var sig *Signature
|
var sig *Signature
|
||||||
for _, sz := range sizes {
|
for _, sz := range sizes {
|
||||||
buf := randbuf(sz)
|
buf := randbuf(sz)
|
||||||
|
@ -265,11 +288,11 @@ func Benchmark_Sig(b *testing.B) {
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
|
|
||||||
b.Run(s0, func(b *testing.B) {
|
b.Run(s0, func(b *testing.B) {
|
||||||
sig = benchSign(b, buf, sk)
|
sig = benchSign(b, buf, &kp.Sec)
|
||||||
})
|
})
|
||||||
|
|
||||||
b.Run(s1, func(b *testing.B) {
|
b.Run(s1, func(b *testing.B) {
|
||||||
benchVerify(b, buf, sig, pk)
|
benchVerify(b, buf, sig, &kp.Pub)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -289,7 +312,7 @@ func benchVerify(b *testing.B, buf []byte, sig *Signature, pk *PublicKey) {
|
||||||
|
|
||||||
func randbuf(sz uint) []byte {
|
func randbuf(sz uint) []byte {
|
||||||
b := make([]byte, sz)
|
b := make([]byte, sz)
|
||||||
randRead(b)
|
randread(b)
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
33
sign/ssh.go
33
sign/ssh.go
|
@ -25,8 +25,8 @@ import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/dchest/bcrypt_pbkdf"
|
"github.com/dchest/bcrypt_pbkdf"
|
||||||
|
@ -34,6 +34,17 @@ import (
|
||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrIncorrectPassword = errors.New("ssh: Invalid Passphrase")
|
||||||
|
ErrNoPEMFound = errors.New("no PEM block found")
|
||||||
|
ErrBadPublicKey = errors.New("ssh: malformed public key")
|
||||||
|
ErrKeyTooShort = errors.New("ssh: public key too short")
|
||||||
|
ErrBadTrailers = errors.New("ssh: trailing junk in public key")
|
||||||
|
ErrBadFormat = errors.New("ssh: invalid openssh private key format")
|
||||||
|
ErrBadLength = errors.New("ssh: private key unexpected length")
|
||||||
|
ErrBadPadding = errors.New("ssh: padding not as expected")
|
||||||
|
)
|
||||||
|
|
||||||
const keySizeAES256 = 32
|
const keySizeAES256 = 32
|
||||||
|
|
||||||
// ParseEncryptedRawPrivateKey returns a private key from an
|
// ParseEncryptedRawPrivateKey returns a private key from an
|
||||||
|
@ -57,13 +68,12 @@ func parseSSHPrivateKey(data []byte, getpw func() ([]byte, error)) (*PrivateKey,
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseSSHPublicKey(in []byte) (*PublicKey, error) {
|
func parseSSHPublicKey(in []byte) (*PublicKey, error) {
|
||||||
splitter := regexp.MustCompile("[ \\t]+")
|
v := bytes.Split(in, []byte(" \t"))
|
||||||
v := splitter.Split(string(in), -1)
|
|
||||||
if len(v) != 3 {
|
if len(v) != 3 {
|
||||||
return nil, ErrBadPublicKey
|
return nil, ErrBadPublicKey
|
||||||
}
|
}
|
||||||
|
|
||||||
return parseEncPubKey([]byte(v[1]), v[2])
|
return parseEncPubKey(v[1], string(v[2]))
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse a wire encoded public key
|
// parse a wire encoded public key
|
||||||
|
@ -95,13 +105,11 @@ func parseEncPubKey(in []byte, comm string) (*PublicKey, error) {
|
||||||
return nil, ErrBadTrailers
|
return nil, ErrBadTrailers
|
||||||
}
|
}
|
||||||
|
|
||||||
var pk PublicKey
|
pk, err := PublicKeyFromBytes(w.KeyBytes)
|
||||||
|
if err == nil {
|
||||||
if err = makePublicKeyFromBytes(&pk, w.KeyBytes); err == nil {
|
|
||||||
pk.Comment = strings.TrimSpace(comm)
|
pk.Comment = strings.TrimSpace(comm)
|
||||||
return &pk, nil
|
|
||||||
}
|
}
|
||||||
return nil, err
|
return pk, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseString(in []byte) (out, rest []byte, ok bool) {
|
func parseString(in []byte) (out, rest []byte, ok bool) {
|
||||||
|
@ -345,11 +353,8 @@ func parseOpenSSHPrivateKey(data []byte, getpw func() ([]byte, error)) (*Private
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var sk PrivateKey
|
pk, err := PrivateKeyFromBytes(key.Priv)
|
||||||
if err = makePrivateKeyFromBytes(&sk, key.Priv); err == nil {
|
return pk, err
|
||||||
return &sk, nil
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("ssh: unhandled key type: %v", pk1.Keytype)
|
return nil, fmt.Errorf("ssh: unhandled key type: %v", pk1.Keytype)
|
||||||
}
|
}
|
||||||
|
|
162
sign/stream.go
162
sign/stream.go
|
@ -1,162 +0,0 @@
|
||||||
// stream.go - Streaming io.Reader, io.Writer interface to encryption/decryption
|
|
||||||
//
|
|
||||||
// (c) 2016 Sudhi Herle <sudhi@herle.net>
|
|
||||||
//
|
|
||||||
// Licensing Terms: GPLv2
|
|
||||||
//
|
|
||||||
// If you need a commercial license for this work, please contact
|
|
||||||
// the author.
|
|
||||||
//
|
|
||||||
// This software does not come with any express or implied
|
|
||||||
// warranty; it is provided "as is". No claim is made to its
|
|
||||||
// suitability for any purpose.
|
|
||||||
//
|
|
||||||
|
|
||||||
package sign
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
// encWriter buffers partial writes until a full chunk is accumulated.
|
|
||||||
// It's methods implement the io.WriteCloser interface.
|
|
||||||
type encWriter struct {
|
|
||||||
buf []byte
|
|
||||||
n int // # of bytes written
|
|
||||||
wr io.WriteCloser
|
|
||||||
e *Encryptor
|
|
||||||
blk uint32
|
|
||||||
err error
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ io.WriteCloser = &encWriter{}
|
|
||||||
|
|
||||||
// NewStreamWriter begins stream encryption to an underlying destination writer 'wr'.
|
|
||||||
// It returns an io.WriteCloser.
|
|
||||||
func (e *Encryptor) NewStreamWriter(wr io.WriteCloser) (io.WriteCloser, error) {
|
|
||||||
if !e.started {
|
|
||||||
err := e.start(wr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
w := &encWriter{
|
|
||||||
buf: make([]byte, e.ChunkSize),
|
|
||||||
wr: wr,
|
|
||||||
e: e,
|
|
||||||
}
|
|
||||||
|
|
||||||
e.stream = true
|
|
||||||
return w, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write implements the io.Writer interface
|
|
||||||
func (w *encWriter) Write(b []byte) (int, error) {
|
|
||||||
if w.err != nil {
|
|
||||||
return 0, w.err
|
|
||||||
}
|
|
||||||
|
|
||||||
n := len(b)
|
|
||||||
if n == 0 {
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
max := int(w.e.ChunkSize)
|
|
||||||
for len(b) > 0 {
|
|
||||||
buf := w.buf[w.n:]
|
|
||||||
z := copy(buf, b)
|
|
||||||
b = b[z:]
|
|
||||||
w.n += z
|
|
||||||
|
|
||||||
// We only flush if we have more data remaining in the input buffer.
|
|
||||||
// This way, we don't flush a potentially last block here; that happens
|
|
||||||
// when the caller eventually closes the stream.
|
|
||||||
if w.n == max && len(b) > 0 {
|
|
||||||
w.err = w.e.encrypt(w.buf, w.wr, w.blk, false)
|
|
||||||
if w.err != nil {
|
|
||||||
return 0, w.err
|
|
||||||
}
|
|
||||||
|
|
||||||
w.n = 0
|
|
||||||
w.blk += 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close implements the io.Close interface
|
|
||||||
func (w *encWriter) Close() error {
|
|
||||||
if w.err != nil {
|
|
||||||
return w.err
|
|
||||||
}
|
|
||||||
|
|
||||||
err := w.e.encrypt(w.buf[:w.n], w.wr, w.blk, true)
|
|
||||||
if err != nil {
|
|
||||||
w.err = err
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
w.n = 0
|
|
||||||
w.err = ErrClosed
|
|
||||||
return w.wr.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
// encReader buffers partial reads and it's methods implement the io.Reader interface.
|
|
||||||
type encReader struct {
|
|
||||||
buf []byte
|
|
||||||
unread []byte
|
|
||||||
d *Decryptor
|
|
||||||
blk uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ io.Reader = &encReader{}
|
|
||||||
|
|
||||||
// NewStreamReader returns an io.Reader to read from the decrypted stream
|
|
||||||
func (d *Decryptor) NewStreamReader() (io.Reader, error) {
|
|
||||||
if d.key == nil {
|
|
||||||
return nil, ErrNoKey
|
|
||||||
}
|
|
||||||
|
|
||||||
if d.eof {
|
|
||||||
return nil, io.EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
d.stream = true
|
|
||||||
return &encReader{
|
|
||||||
buf: make([]byte, d.ChunkSize),
|
|
||||||
d: d,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read implements io.Reader interface
|
|
||||||
func (r *encReader) Read(b []byte) (int, error) {
|
|
||||||
if r.d.eof && len(r.unread) == 0 {
|
|
||||||
return 0, io.EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(r.unread) > 0 {
|
|
||||||
n := copy(b, r.unread)
|
|
||||||
r.unread = r.unread[n:]
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
buf, eof, err := r.d.decrypt(r.blk)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
r.blk += 1
|
|
||||||
|
|
||||||
n := copy(b, buf)
|
|
||||||
buf = buf[n:]
|
|
||||||
|
|
||||||
copy(r.buf, buf)
|
|
||||||
r.unread = r.buf[:len(buf)]
|
|
||||||
|
|
||||||
if eof {
|
|
||||||
r.d.eof = true
|
|
||||||
}
|
|
||||||
|
|
||||||
return n, nil
|
|
||||||
}
|
|
|
@ -1,43 +0,0 @@
|
||||||
// utils_test.go -- Test harness utilities for sign
|
|
||||||
//
|
|
||||||
// (c) 2016 Sudhi Herle <sudhi@herle.net>
|
|
||||||
//
|
|
||||||
// Licensing Terms: GPLv2
|
|
||||||
//
|
|
||||||
// If you need a commercial license for this work, please contact
|
|
||||||
// the author.
|
|
||||||
//
|
|
||||||
// This software does not come with any express or implied
|
|
||||||
// warranty; it is provided "as is". No claim is made to its
|
|
||||||
// suitability for any purpose.
|
|
||||||
|
|
||||||
package sign
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/subtle"
|
|
||||||
"fmt"
|
|
||||||
"runtime"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func newAsserter(t *testing.T) func(cond bool, msg string, args ...interface{}) {
|
|
||||||
return func(cond bool, msg string, args ...interface{}) {
|
|
||||||
if cond {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
_, file, line, ok := runtime.Caller(1)
|
|
||||||
if !ok {
|
|
||||||
file = "???"
|
|
||||||
line = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
s := fmt.Sprintf(msg, args...)
|
|
||||||
t.Fatalf("%s: %d: Assertion failed: %s\n", file, line, s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return true if two byte arrays are equal
|
|
||||||
func byteEq(x, y []byte) bool {
|
|
||||||
return subtle.ConstantTimeCompare(x, y) == 1
|
|
||||||
}
|
|
362
sigtool.go
Normal file
362
sigtool.go
Normal file
|
@ -0,0 +1,362 @@
|
||||||
|
// sigtool.go -- Tool to generate, manage Ed25519 keys and
|
||||||
|
// signatures.
|
||||||
|
//
|
||||||
|
// (c) 2016 Sudhi Herle <sudhi@herle.net>
|
||||||
|
//
|
||||||
|
// Licensing Terms: GPLv2
|
||||||
|
//
|
||||||
|
// If you need a commercial license for this work, please contact
|
||||||
|
// the author.
|
||||||
|
//
|
||||||
|
// This software does not come with any express or implied
|
||||||
|
// warranty; it is provided "as is". No claim is made to its
|
||||||
|
// suitability for any purpose.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/opencoff/go-utils"
|
||||||
|
flag "github.com/opencoff/pflag"
|
||||||
|
"github.com/opencoff/sigtool/sign"
|
||||||
|
)
|
||||||
|
|
||||||
|
var Z string = path.Base(os.Args[0])
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
var ver, help bool
|
||||||
|
|
||||||
|
mf := flag.NewFlagSet(Z, flag.ExitOnError)
|
||||||
|
mf.SetInterspersed(false)
|
||||||
|
mf.BoolVarP(&ver, "version", "v", false, "Show version info and exit")
|
||||||
|
mf.BoolVarP(&help, "help", "h", false, "Show help info exit")
|
||||||
|
mf.Parse(os.Args[1:])
|
||||||
|
|
||||||
|
if ver {
|
||||||
|
fmt.Printf("%s - %s [%s; %s]\n", Z, ProductVersion, RepoVersion, Buildtime)
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
if help {
|
||||||
|
usage(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
args := mf.Args()
|
||||||
|
if len(args) < 1 {
|
||||||
|
warn("Insufficient arguments. Try '%s -h'", Z)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmds := map[string]func(args []string){
|
||||||
|
"generate": gen,
|
||||||
|
"sign": signify,
|
||||||
|
"verify": verify,
|
||||||
|
"encrypt": encrypt,
|
||||||
|
"decrypt": decrypt,
|
||||||
|
|
||||||
|
"help": func(_ []string) {
|
||||||
|
usage(0)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
words := make([]string, 0, len(cmds))
|
||||||
|
for k := range cmds {
|
||||||
|
words = append(words, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
ab := utils.Abbrev(words)
|
||||||
|
canon, ok := ab[strings.ToLower(args[0])]
|
||||||
|
if !ok {
|
||||||
|
die("Unknown command %s", args[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := cmds[canon]
|
||||||
|
if cmd == nil {
|
||||||
|
die("can't map command %s", canon)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd(args[1:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the generate command
|
||||||
|
func gen(args []string) {
|
||||||
|
|
||||||
|
var nopw, help, force bool
|
||||||
|
var comment string
|
||||||
|
var envpw string
|
||||||
|
|
||||||
|
fs := flag.NewFlagSet("generate", flag.ExitOnError)
|
||||||
|
fs.BoolVarP(&help, "help", "h", false, "Show this help and exit")
|
||||||
|
fs.BoolVarP(&nopw, "no-password", "", false, "Don't ask for a password for the private key")
|
||||||
|
fs.StringVarP(&comment, "comment", "c", "", "Use `C` as the text comment for the keys")
|
||||||
|
fs.StringVarP(&envpw, "env-password", "E", "", "Use passphrase from environment variable `E`")
|
||||||
|
fs.BoolVarP(&force, "force", "F", false, "Overwrite the output file if it exists")
|
||||||
|
|
||||||
|
fs.Parse(args)
|
||||||
|
|
||||||
|
if help {
|
||||||
|
fs.SetOutput(os.Stdout)
|
||||||
|
fmt.Printf(`%s generate|gen|g [options] file-prefix
|
||||||
|
|
||||||
|
Generate a new Ed25519 public+private key pair and write public key to
|
||||||
|
FILE-PREFIX.pub and private key to FILE-PREFIX.key.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
`, Z)
|
||||||
|
fs.PrintDefaults()
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
args = fs.Args()
|
||||||
|
if len(args) < 1 {
|
||||||
|
die("Insufficient arguments to 'generate'. Try '%s generate -h' ..", Z)
|
||||||
|
}
|
||||||
|
|
||||||
|
bn := args[0]
|
||||||
|
|
||||||
|
if exists(bn) && !force {
|
||||||
|
die("Public/Private key files (%s.key, %s.pub) exist. Won't overwrite!", bn, bn)
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
|
||||||
|
kp, err := sign.NewKeypair()
|
||||||
|
if err != nil {
|
||||||
|
die("%s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = kp.Serialize(bn, comment, func() ([]byte, error) {
|
||||||
|
if nopw {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var pws string
|
||||||
|
if len(envpw) > 0 {
|
||||||
|
pws = os.Getenv(envpw)
|
||||||
|
} else {
|
||||||
|
pws, err = utils.Askpass("Enter passphrase for private key", true)
|
||||||
|
if err != nil {
|
||||||
|
die("%s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return []byte(pws), nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
die("%s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the 'sign' command.
|
||||||
|
func signify(args []string) {
|
||||||
|
var nopw, help bool
|
||||||
|
var output string
|
||||||
|
var envpw string
|
||||||
|
|
||||||
|
fs := flag.NewFlagSet("sign", flag.ExitOnError)
|
||||||
|
fs.BoolVarP(&help, "help", "h", false, "Show this help and exit")
|
||||||
|
fs.BoolVarP(&nopw, "no-password", "", false, "Don't ask for a password for the private key")
|
||||||
|
fs.StringVarP(&envpw, "env-password", "E", "", "Use passphrase from environment variable `E`")
|
||||||
|
fs.StringVarP(&output, "output", "o", "", "Write signature to file `F`")
|
||||||
|
|
||||||
|
fs.Parse(args)
|
||||||
|
|
||||||
|
if help {
|
||||||
|
fs.SetOutput(os.Stdout)
|
||||||
|
fmt.Printf(`%s sign|s [options] privkey file
|
||||||
|
|
||||||
|
Sign FILE with a Ed25519 private key PRIVKEY and write signature to FILE.sig
|
||||||
|
|
||||||
|
Options:
|
||||||
|
`, Z)
|
||||||
|
fs.PrintDefaults()
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
args = fs.Args()
|
||||||
|
if len(args) < 2 {
|
||||||
|
die("Insufficient arguments to 'sign'. Try '%s sign -h' ..", Z)
|
||||||
|
}
|
||||||
|
|
||||||
|
kn := args[0]
|
||||||
|
fn := args[1]
|
||||||
|
outf := fmt.Sprintf("%s.sig", fn)
|
||||||
|
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if len(output) > 0 {
|
||||||
|
outf = output
|
||||||
|
}
|
||||||
|
|
||||||
|
sk, err := sign.ReadPrivateKey(kn, func() ([]byte, error) {
|
||||||
|
if nopw {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var pws string
|
||||||
|
if len(envpw) > 0 {
|
||||||
|
pws = os.Getenv(envpw)
|
||||||
|
} else {
|
||||||
|
pws, err = utils.Askpass("Enter passphrase for private key", false)
|
||||||
|
if err != nil {
|
||||||
|
die("%s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return []byte(pws), nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
die("%s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sig, err := sk.SignFile(fn)
|
||||||
|
if err != nil {
|
||||||
|
die("%s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sigo, err := sig.Serialize(fmt.Sprintf("input=%s", fn))
|
||||||
|
|
||||||
|
var fd io.Writer = os.Stdout
|
||||||
|
|
||||||
|
if outf != "-" {
|
||||||
|
fdx, err := os.OpenFile(outf, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
|
||||||
|
if err != nil {
|
||||||
|
die("can't create output file %s: %s", outf, err)
|
||||||
|
}
|
||||||
|
defer fdx.Close()
|
||||||
|
fd = fdx
|
||||||
|
}
|
||||||
|
|
||||||
|
fd.Write(sigo)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify signature on a given file
|
||||||
|
func verify(args []string) {
|
||||||
|
var help, quiet bool
|
||||||
|
|
||||||
|
fs := flag.NewFlagSet("verify", flag.ExitOnError)
|
||||||
|
fs.BoolVarP(&help, "help", "h", false, "Show this help and exit")
|
||||||
|
fs.BoolVarP(&quiet, "quiet", "q", false, "Don't show any output; exit with status code only")
|
||||||
|
|
||||||
|
fs.Parse(args)
|
||||||
|
|
||||||
|
if help {
|
||||||
|
fs.SetOutput(os.Stdout)
|
||||||
|
fmt.Printf(`%s verify|v [options] pubkey sig file
|
||||||
|
|
||||||
|
Verify an Ed25519 signature in SIG of FILE using a public key PUBKEY.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
`, Z)
|
||||||
|
fs.PrintDefaults()
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
args = fs.Args()
|
||||||
|
if len(args) < 3 {
|
||||||
|
die("Insufficient arguments to 'verify'. Try '%s verify -h' ..", Z)
|
||||||
|
}
|
||||||
|
|
||||||
|
pn := args[0]
|
||||||
|
sn := args[1]
|
||||||
|
fn := args[2]
|
||||||
|
|
||||||
|
sig, err := sign.ReadSignature(sn)
|
||||||
|
if err != nil {
|
||||||
|
die("Can't read signature '%s': %s", sn, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pk, err := sign.ReadPublicKey(pn)
|
||||||
|
if err != nil {
|
||||||
|
die("%s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !sig.IsPKMatch(pk) {
|
||||||
|
die("Wrong public key '%s' for verifying '%s'", pn, sn)
|
||||||
|
}
|
||||||
|
|
||||||
|
ok, err := pk.VerifyFile(fn, sig)
|
||||||
|
if err != nil {
|
||||||
|
die("%s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
exit := 0
|
||||||
|
if !ok {
|
||||||
|
exit = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if !quiet {
|
||||||
|
if ok {
|
||||||
|
fmt.Printf("%s: Signature %s verified\n", fn, sn)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("%s: Signature %s verification failure\n", fn, sn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Exit(exit)
|
||||||
|
}
|
||||||
|
|
||||||
|
func usage(c int) {
|
||||||
|
x := fmt.Sprintf(`%s is a tool to generate, sign and verify files with Ed25519 signatures.
|
||||||
|
|
||||||
|
Usage: %s [global-options] command [options] arg [args..]
|
||||||
|
|
||||||
|
Global options:
|
||||||
|
-h, --help Show help and exit
|
||||||
|
-v, --version Show version info and exit.
|
||||||
|
|
||||||
|
Commands:
|
||||||
|
generate, g Generate a new Ed25519 keypair
|
||||||
|
sign, s Sign a file with a private key
|
||||||
|
verify, v Verify a signature against a file and a public key
|
||||||
|
encrypt, e Encrypt an input file to one or more recipients
|
||||||
|
decrypt, d Decrypt a file with a private key
|
||||||
|
`, Z, Z)
|
||||||
|
|
||||||
|
os.Stdout.Write([]byte(x))
|
||||||
|
os.Exit(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return true if $bn.key or $bn.pub exist; false otherwise
|
||||||
|
func exists(bn string) bool {
|
||||||
|
pk := bn + ".pub"
|
||||||
|
sk := bn + ".key"
|
||||||
|
|
||||||
|
if _, err := os.Stat(pk); err == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if _, err := os.Stat(sk); err == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// die with error
|
||||||
|
func die(f string, v ...interface{}) {
|
||||||
|
warn(f, v...)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func warn(f string, v ...interface{}) {
|
||||||
|
z := fmt.Sprintf("%s: %s", os.Args[0], f)
|
||||||
|
s := fmt.Sprintf(z, v...)
|
||||||
|
if n := len(s); s[n-1] != '\n' {
|
||||||
|
s += "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Stderr.WriteString(s)
|
||||||
|
os.Stderr.Sync()
|
||||||
|
}
|
||||||
|
|
||||||
|
// This will be filled in by "build"
|
||||||
|
var RepoVersion string = "UNDEFINED"
|
||||||
|
var Buildtime string = "UNDEFINED"
|
||||||
|
var ProductVersion string = "UNDEFINED"
|
||||||
|
|
||||||
|
// vim: ft=go:sw=8:ts=8:noexpandtab:tw=98:
|
55
src/die.go
55
src/die.go
|
@ -1,55 +0,0 @@
|
||||||
// die.go -- die() and warn()
|
|
||||||
//
|
|
||||||
// (c) 2016 Sudhi Herle <sudhi@herle.net>
|
|
||||||
//
|
|
||||||
// Licensing Terms: GPLv2
|
|
||||||
//
|
|
||||||
// If you need a commercial license for this work, please contact
|
|
||||||
// the author.
|
|
||||||
//
|
|
||||||
// This software does not come with any express or implied
|
|
||||||
// warranty; it is provided "as is". No claim is made to its
|
|
||||||
// suitability for any purpose.
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
var atExit []func()
|
|
||||||
|
|
||||||
// Die prints an error message to stderr
|
|
||||||
// and exits the program after calling all the registered
|
|
||||||
// at-exit functions.
|
|
||||||
func Die(f string, v ...interface{}) {
|
|
||||||
Warn(f, v...)
|
|
||||||
Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Warn prints an error message to stderr
|
|
||||||
func Warn(f string, v ...interface{}) {
|
|
||||||
z := fmt.Sprintf("%s: %s", os.Args[0], f)
|
|
||||||
s := fmt.Sprintf(z, v...)
|
|
||||||
if n := len(s); s[n-1] != '\n' {
|
|
||||||
s += "\n"
|
|
||||||
}
|
|
||||||
|
|
||||||
os.Stderr.WriteString(s)
|
|
||||||
os.Stderr.Sync()
|
|
||||||
}
|
|
||||||
|
|
||||||
// AtExit registers a function to be called before the program exits.
|
|
||||||
func AtExit(f func()) {
|
|
||||||
atExit = append(atExit, f)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exit invokes the registered atexit handlers and exits with the
|
|
||||||
// given code.
|
|
||||||
func Exit(v int) {
|
|
||||||
for _, f := range atExit {
|
|
||||||
f()
|
|
||||||
}
|
|
||||||
os.Exit(v)
|
|
||||||
}
|
|
100
src/gen.go
100
src/gen.go
|
@ -1,100 +0,0 @@
|
||||||
// gen.go -- generate keys
|
|
||||||
//
|
|
||||||
// (c) 2016 Sudhi Herle <sudhi@herle.net>
|
|
||||||
//
|
|
||||||
// Licensing Terms: GPLv2
|
|
||||||
//
|
|
||||||
// If you need a commercial license for this work, please contact
|
|
||||||
// the author.
|
|
||||||
//
|
|
||||||
// This software does not come with any express or implied
|
|
||||||
// warranty; it is provided "as is". No claim is made to its
|
|
||||||
// suitability for any purpose.
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
|
|
||||||
"git.rgst.io/homelab/sigtool/v3/sign"
|
|
||||||
"github.com/opencoff/go-utils"
|
|
||||||
flag "github.com/opencoff/pflag"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Run the generate command
|
|
||||||
func gen(args []string) {
|
|
||||||
var nopw, help, force bool
|
|
||||||
var comment string
|
|
||||||
var envpw string
|
|
||||||
|
|
||||||
fs := flag.NewFlagSet("generate", flag.ExitOnError)
|
|
||||||
fs.BoolVarP(&help, "help", "h", false, "Show this help and exit")
|
|
||||||
fs.BoolVarP(&nopw, "no-password", "", false, "Don't ask for a password for the private key")
|
|
||||||
fs.StringVarP(&comment, "comment", "c", "", "Use `C` as the text comment for the keys")
|
|
||||||
fs.StringVarP(&envpw, "env-password", "E", "", "Use passphrase from environment variable `E`")
|
|
||||||
fs.BoolVarP(&force, "overwrite", "", false, "Overwrite the output file if it exists")
|
|
||||||
|
|
||||||
fs.Parse(args)
|
|
||||||
|
|
||||||
if help {
|
|
||||||
fs.SetOutput(os.Stdout)
|
|
||||||
fmt.Printf(`%s generate|gen|g [options] file-prefix
|
|
||||||
|
|
||||||
Generate a new Ed25519 public+private key pair and write public key to
|
|
||||||
FILE-PREFIX.pub and private key to FILE-PREFIX.key.
|
|
||||||
|
|
||||||
Options:
|
|
||||||
`, Z)
|
|
||||||
fs.PrintDefaults()
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
args = fs.Args()
|
|
||||||
if len(args) < 1 {
|
|
||||||
Die("Insufficient arguments to 'generate'. Try '%s generate -h' ..", Z)
|
|
||||||
}
|
|
||||||
|
|
||||||
bn := args[0]
|
|
||||||
|
|
||||||
pkn := fmt.Sprintf("%s.pub", path.Clean(bn))
|
|
||||||
skn := fmt.Sprintf("%s.key", path.Clean(bn))
|
|
||||||
|
|
||||||
if !force {
|
|
||||||
if exists(pkn) || exists(skn) {
|
|
||||||
Die("Public/Private key files (%s, %s) exist. won't overwrite!", skn, pkn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var err error
|
|
||||||
var pw []byte
|
|
||||||
|
|
||||||
if !nopw {
|
|
||||||
var pws string
|
|
||||||
if len(envpw) > 0 {
|
|
||||||
pws = os.Getenv(envpw)
|
|
||||||
} else {
|
|
||||||
pws, err = utils.Askpass("Enter passphrase for private key", true)
|
|
||||||
if err != nil {
|
|
||||||
Die("%s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pw = []byte(pws)
|
|
||||||
}
|
|
||||||
|
|
||||||
sk, err := sign.NewPrivateKey()
|
|
||||||
if err != nil {
|
|
||||||
Die("%s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = sk.Serialize(skn, comment, force, pw); err != nil {
|
|
||||||
Die("%s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
pk := sk.PublicKey()
|
|
||||||
if err = pk.Serialize(pkn, comment, force); err != nil {
|
|
||||||
Die("%s", err)
|
|
||||||
}
|
|
||||||
}
|
|
116
src/sign.go
116
src/sign.go
|
@ -1,116 +0,0 @@
|
||||||
// sign.go -- 'sign' command implementation
|
|
||||||
//
|
|
||||||
// (c) 2016 Sudhi Herle <sudhi@herle.net>
|
|
||||||
//
|
|
||||||
// Licensing Terms: GPLv2
|
|
||||||
//
|
|
||||||
// If you need a commercial license for this work, please contact
|
|
||||||
// the author.
|
|
||||||
//
|
|
||||||
// This software does not come with any express or implied
|
|
||||||
// warranty; it is provided "as is". No claim is made to its
|
|
||||||
// suitability for any purpose.
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"git.rgst.io/homelab/sigtool/v3/sign"
|
|
||||||
"github.com/opencoff/go-fio"
|
|
||||||
"github.com/opencoff/go-utils"
|
|
||||||
flag "github.com/opencoff/pflag"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Run the 'sign' command.
|
|
||||||
func signify(args []string) {
|
|
||||||
var nopw, help, force bool
|
|
||||||
var output string
|
|
||||||
var envpw string
|
|
||||||
|
|
||||||
fs := flag.NewFlagSet("sign", flag.ExitOnError)
|
|
||||||
fs.BoolVarP(&help, "help", "h", false, "Show this help and exit")
|
|
||||||
fs.BoolVarP(&nopw, "no-password", "", false, "Don't ask for a password for the private key")
|
|
||||||
fs.StringVarP(&envpw, "env-password", "E", "", "Use passphrase from environment variable `E`")
|
|
||||||
fs.StringVarP(&output, "output", "o", "", "Write signature to file `F`")
|
|
||||||
fs.BoolVarP(&force, "overwrite", "", false, "Overwrite previous signature file if it exists")
|
|
||||||
|
|
||||||
fs.Parse(args)
|
|
||||||
|
|
||||||
if help {
|
|
||||||
fs.SetOutput(os.Stdout)
|
|
||||||
fmt.Printf(`%s sign|s [options] privkey file
|
|
||||||
|
|
||||||
Sign FILE with a Ed25519 private key PRIVKEY and write signature to FILE.sig
|
|
||||||
|
|
||||||
Options:
|
|
||||||
`, Z)
|
|
||||||
fs.PrintDefaults()
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
args = fs.Args()
|
|
||||||
if len(args) < 2 {
|
|
||||||
Die("Insufficient arguments to 'sign'. Try '%s sign -h' ..", Z)
|
|
||||||
}
|
|
||||||
|
|
||||||
kn := args[0]
|
|
||||||
fn := args[1]
|
|
||||||
outf := fmt.Sprintf("%s.sig", fn)
|
|
||||||
|
|
||||||
var err error
|
|
||||||
|
|
||||||
if len(output) > 0 {
|
|
||||||
outf = output
|
|
||||||
}
|
|
||||||
|
|
||||||
var fd io.WriteCloser = os.Stdout
|
|
||||||
|
|
||||||
if outf != "-" {
|
|
||||||
var opts uint32
|
|
||||||
if force {
|
|
||||||
opts |= fio.OPT_OVERWRITE
|
|
||||||
}
|
|
||||||
sf, err := fio.NewSafeFile(outf, opts, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
|
|
||||||
if err != nil {
|
|
||||||
Die("can't create sig file: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// we unlink and remove temp on any error
|
|
||||||
AtExit(sf.Abort)
|
|
||||||
defer sf.Abort()
|
|
||||||
fd = sf
|
|
||||||
}
|
|
||||||
|
|
||||||
sk, err := sign.ReadPrivateKey(kn, func() ([]byte, error) {
|
|
||||||
if nopw {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var pws string
|
|
||||||
if len(envpw) > 0 {
|
|
||||||
pws = os.Getenv(envpw)
|
|
||||||
} else {
|
|
||||||
pws, err = utils.Askpass("Enter passphrase for private key", false)
|
|
||||||
if err != nil {
|
|
||||||
Die("%s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return []byte(pws), nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
Die("%s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
sig, err := sk.SignFile(fn)
|
|
||||||
if err != nil {
|
|
||||||
Die("%s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
sigbytes, err := sig.MarshalBinary(fmt.Sprintf("input=%s", fn))
|
|
||||||
fd.Write(sigbytes)
|
|
||||||
fd.Close()
|
|
||||||
}
|
|
129
src/sigtool.go
129
src/sigtool.go
|
@ -1,129 +0,0 @@
|
||||||
// sigtool.go -- Tool to generate, manage Ed25519 keys and
|
|
||||||
// signatures.
|
|
||||||
//
|
|
||||||
// (c) 2016 Sudhi Herle <sudhi@herle.net>
|
|
||||||
//
|
|
||||||
// Licensing Terms: GPLv2
|
|
||||||
//
|
|
||||||
// If you need a commercial license for this work, please contact
|
|
||||||
// the author.
|
|
||||||
//
|
|
||||||
// This software does not come with any express or implied
|
|
||||||
// warranty; it is provided "as is". No claim is made to its
|
|
||||||
// suitability for any purpose.
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"git.rgst.io/homelab/sigtool/v3/sign"
|
|
||||||
"github.com/opencoff/go-utils"
|
|
||||||
flag "github.com/opencoff/pflag"
|
|
||||||
)
|
|
||||||
|
|
||||||
var Z string = path.Base(os.Args[0])
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
var ver, help, debug bool
|
|
||||||
|
|
||||||
mf := flag.NewFlagSet(Z, flag.ExitOnError)
|
|
||||||
mf.SetInterspersed(false)
|
|
||||||
mf.BoolVarP(&ver, "version", "v", false, "Show version info and exit")
|
|
||||||
mf.BoolVarP(&help, "help", "h", false, "Show help info exit")
|
|
||||||
mf.BoolVarP(&debug, "debug", "", false, "Enable debug mode")
|
|
||||||
mf.Parse(os.Args[1:])
|
|
||||||
|
|
||||||
if ver {
|
|
||||||
fmt.Printf("%s - %s [%s]\n", Z, ProductVersion, RepoVersion)
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
if help {
|
|
||||||
usage(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
args := mf.Args()
|
|
||||||
if len(args) < 1 {
|
|
||||||
Die("Insufficient arguments. Try '%s -h'", Z)
|
|
||||||
}
|
|
||||||
|
|
||||||
cmds := map[string]func(args []string){
|
|
||||||
"generate": gen,
|
|
||||||
"sign": signify,
|
|
||||||
"verify": verify,
|
|
||||||
"encrypt": encrypt,
|
|
||||||
"decrypt": decrypt,
|
|
||||||
|
|
||||||
"help": func(_ []string) {
|
|
||||||
usage(0)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
words := make([]string, 0, len(cmds))
|
|
||||||
for k := range cmds {
|
|
||||||
words = append(words, k)
|
|
||||||
}
|
|
||||||
|
|
||||||
ab := utils.Abbrev(words)
|
|
||||||
canon, ok := ab[strings.ToLower(args[0])]
|
|
||||||
if !ok {
|
|
||||||
Die("Unknown command %s", args[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd := cmds[canon]
|
|
||||||
if cmd == nil {
|
|
||||||
Die("can't map command %s", canon)
|
|
||||||
}
|
|
||||||
|
|
||||||
if debug {
|
|
||||||
sign.Debug(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd(args[1:])
|
|
||||||
|
|
||||||
// always call Exit so that at-exit handlers are called.
|
|
||||||
Exit(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify signature on a given file
|
|
||||||
|
|
||||||
func usage(c int) {
|
|
||||||
x := fmt.Sprintf(`%s is a tool to generate, sign and verify files with Ed25519 signatures.
|
|
||||||
|
|
||||||
Usage: %s [global-options] command [options] arg [args..]
|
|
||||||
|
|
||||||
Global options:
|
|
||||||
-h, --help Show help and exit
|
|
||||||
-v, --version Show version info and exit
|
|
||||||
--debug Enable debug (DANGEROUS)
|
|
||||||
|
|
||||||
Commands:
|
|
||||||
generate, g Generate a new Ed25519 keypair
|
|
||||||
sign, s Sign a file with a private key
|
|
||||||
verify, v Verify a signature against a file and a public key
|
|
||||||
encrypt, e Encrypt an input file to one or more recipients
|
|
||||||
decrypt, d Decrypt a file with a private key
|
|
||||||
`, Z, Z)
|
|
||||||
|
|
||||||
os.Stdout.Write([]byte(x))
|
|
||||||
os.Exit(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return true if $bn.key or $bn.pub exist; false otherwise
|
|
||||||
func exists(nm string) bool {
|
|
||||||
if _, err := os.Stat(nm); err == nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// This will be filled in by "build"
|
|
||||||
var RepoVersion string = "UNDEFINED"
|
|
||||||
var ProductVersion string = "UNDEFINED"
|
|
||||||
|
|
||||||
// vim: ft=go:sw=8:ts=8:noexpandtab:tw=98:
|
|
|
@ -1,96 +0,0 @@
|
||||||
// verify.go -- Verify signatures
|
|
||||||
//
|
|
||||||
// (c) 2016 Sudhi Herle <sudhi@herle.net>
|
|
||||||
//
|
|
||||||
// Licensing Terms: GPLv2
|
|
||||||
//
|
|
||||||
// If you need a commercial license for this work, please contact
|
|
||||||
// the author.
|
|
||||||
//
|
|
||||||
// This software does not come with any express or implied
|
|
||||||
// warranty; it is provided "as is". No claim is made to its
|
|
||||||
// suitability for any purpose.
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"git.rgst.io/homelab/sigtool/v3/sign"
|
|
||||||
flag "github.com/opencoff/pflag"
|
|
||||||
)
|
|
||||||
|
|
||||||
func verify(args []string) {
|
|
||||||
var help, quiet bool
|
|
||||||
|
|
||||||
fs := flag.NewFlagSet("verify", flag.ExitOnError)
|
|
||||||
fs.BoolVarP(&help, "help", "h", false, "Show this help and exit")
|
|
||||||
fs.BoolVarP(&quiet, "quiet", "q", false, "Don't show any output; exit with status code only")
|
|
||||||
|
|
||||||
fs.Parse(args)
|
|
||||||
|
|
||||||
if help {
|
|
||||||
fs.SetOutput(os.Stdout)
|
|
||||||
fmt.Printf(`%s verify|v [options] pubkey sig file
|
|
||||||
|
|
||||||
Verify an Ed25519 signature in SIG of FILE using a public key PUBKEY.
|
|
||||||
The pubkey can be one of:
|
|
||||||
- a file: either OpenSSH ed25519 pubkey or a sigtool pubkey
|
|
||||||
- a string: the raw OpenSSH or sigtool pubkey
|
|
||||||
|
|
||||||
%s will first parse it as a string before trying to parse it as a file.
|
|
||||||
|
|
||||||
Options:
|
|
||||||
`, Z, Z)
|
|
||||||
fs.PrintDefaults()
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
args = fs.Args()
|
|
||||||
if len(args) < 3 {
|
|
||||||
Die("Insufficient arguments to 'verify'. Try '%s verify -h' ..", Z)
|
|
||||||
}
|
|
||||||
|
|
||||||
pn := args[0]
|
|
||||||
sn := args[1]
|
|
||||||
fn := args[2]
|
|
||||||
|
|
||||||
// We first try to read the public key as a base64/openssh string
|
|
||||||
pk, err := sign.MakePublicKeyFromString(pn)
|
|
||||||
if err != nil {
|
|
||||||
pk, err = sign.ReadPublicKey(pn)
|
|
||||||
if err != nil {
|
|
||||||
Die("%s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sig, err := sign.ReadSignature(sn)
|
|
||||||
if err != nil {
|
|
||||||
Die("Can't read signature '%s': %s", sn, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !sig.IsPKMatch(pk) {
|
|
||||||
Die("Wrong public key '%s' for verifying '%s'", pn, sn)
|
|
||||||
}
|
|
||||||
|
|
||||||
ok, err := pk.VerifyFile(fn, sig)
|
|
||||||
if err != nil {
|
|
||||||
Die("%s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
exit := 0
|
|
||||||
if !ok {
|
|
||||||
exit = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if !quiet {
|
|
||||||
if ok {
|
|
||||||
fmt.Printf("%s: Signature %s verified\n", fn, sn)
|
|
||||||
} else {
|
|
||||||
fmt.Printf("%s: Signature %s verification failure\n", fn, sn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
os.Exit(exit)
|
|
||||||
}
|
|
124
tests.sh
124
tests.sh
|
@ -1,124 +0,0 @@
|
||||||
#! /usr/bin/env bash
|
|
||||||
# simple round-trip tests to verify the tool
|
|
||||||
# Usage:
|
|
||||||
# $0 [bin=/path/to/sigtool] [tmpdir=/path/to/workdir]
|
|
||||||
|
|
||||||
Z=`basename $0`
|
|
||||||
die() {
|
|
||||||
echo "$Z: $@" 1>&2
|
|
||||||
echo "$Z: Test output in $tmpdir .." 1>&2
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# cmd line args processing
|
|
||||||
for a in $*; do
|
|
||||||
key=${a%=*}
|
|
||||||
val=${a#*=}
|
|
||||||
case $key in
|
|
||||||
bin)
|
|
||||||
bin=$val
|
|
||||||
;;
|
|
||||||
|
|
||||||
tmpdir)
|
|
||||||
tmpdir=$val
|
|
||||||
;;
|
|
||||||
|
|
||||||
*)
|
|
||||||
echo "Ignoring $key .."
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ -z "$bin" ]; then
|
|
||||||
arch=`./build --print-arch`
|
|
||||||
bin=./bin/$arch/sigtool
|
|
||||||
|
|
||||||
[ -x $bin ] || ./build || die "can't find & build sigtool"
|
|
||||||
fi
|
|
||||||
|
|
||||||
[ -z "$tmpdir" ] && tmpdir=/tmp/sigtool$$
|
|
||||||
|
|
||||||
mkdir -p $tmpdir || die "can't mkdir $tmpdir"
|
|
||||||
|
|
||||||
# env name for reading the password
|
|
||||||
passenv=FOO
|
|
||||||
|
|
||||||
# this is the password for SKs
|
|
||||||
FOO=bar
|
|
||||||
|
|
||||||
|
|
||||||
#trap "rm -rf $tmpdir" EXIT
|
|
||||||
|
|
||||||
bn=$tmpdir/foo
|
|
||||||
sig=$tmpdir/$Z.sig
|
|
||||||
pk=$bn.pub
|
|
||||||
sk=$bn.key
|
|
||||||
bn2=$tmpdir/bar
|
|
||||||
pk2=$bn2.pub
|
|
||||||
sk2=$bn2.key
|
|
||||||
|
|
||||||
encout=$tmpdir/$Z.enc
|
|
||||||
decout=$tmpdir/$Z.dec
|
|
||||||
|
|
||||||
# exit on any failure
|
|
||||||
set -e
|
|
||||||
|
|
||||||
# Now try with ssh ed25519 keys
|
|
||||||
keygen=`which ssh-keygen`
|
|
||||||
[ -z "$keygen" ] && die "can't find ssh-keygen"
|
|
||||||
|
|
||||||
ssk1=$tmpdir/ssk1
|
|
||||||
spk1=$ssk1.pub
|
|
||||||
|
|
||||||
ssk2=$tmpdir/ssk2
|
|
||||||
spk2=$ssk2.pub
|
|
||||||
|
|
||||||
# first generate two ssh keys
|
|
||||||
$keygen -q -C 'ssk1@foo' -t ed25519 -f $ssk1 -N ""
|
|
||||||
$keygen -q -C 'ssk2@foo' -t ed25519 -f $ssk2 -N ""
|
|
||||||
|
|
||||||
# extract the pk string
|
|
||||||
spk1_str=$(cat $spk1 | awk '{ print $2 }')
|
|
||||||
|
|
||||||
$bin s --no-password $ssk1 -o $sig $0 || die "can't sign with $ssk1"
|
|
||||||
$bin v -q $spk1 $sig $0 || die "can't verify with $spk2"
|
|
||||||
$bin v -q $spk1_str $sig $0 || die "can't verify with $spk2_str"
|
|
||||||
|
|
||||||
$bin e --no-password -o $encout $spk2 $0 || die "can't encrypt to $spk2 with $ssk1"
|
|
||||||
$bin d --no-password -o $decout $ssk2 $encout || die "can't decrypt with $ssk2"
|
|
||||||
|
|
||||||
# cleanup state
|
|
||||||
rm -f $sig $encout $decout
|
|
||||||
|
|
||||||
# generate keys
|
|
||||||
$bin g -E FOO $bn || die "can't gen keypair $pk, $sk"
|
|
||||||
$bin g -E FOO $bn 2>/dev/null && die "overwrote prev keypair"
|
|
||||||
$bin g -E FOO --overwrite $bn || die "can't force gen keypair $pk, $sk"
|
|
||||||
$bin g -E FOO $bn2 || die "can't force gen keypair $pk2, $sk2"
|
|
||||||
|
|
||||||
# extract pk string
|
|
||||||
pk_str=$(cat $pk | grep 'pk:' | sed -e 's/^pk: //g')
|
|
||||||
pk2_str=$(cat $pk2 | grep 'pk:' | sed -e 's/^pk: //g')
|
|
||||||
|
|
||||||
# sign and verify
|
|
||||||
$bin s -E FOO $sk $0 -o $sig || die "can't sign $0"
|
|
||||||
$bin v -q $pk $sig $0 || die "can't verify signature of $0"
|
|
||||||
$bin v -q $pk_str $sig $0 || die "can't verify signature of $0"
|
|
||||||
$bin v -q $pk2 $sig $0 2>/dev/null && die "bad verification with wrong $pk2"
|
|
||||||
$bin v -q $pk2_str $sig $0 2>/dev/null && die "bad verification with wrong $pk2"
|
|
||||||
|
|
||||||
# encrypt/decrypt
|
|
||||||
$bin e -E FOO -o $encout $pk2 $0 || die "can't encrypt to $pk2"
|
|
||||||
$bin d -E FOO -o $decout $sk2 $encout || die "can't decrypt with $sk2"
|
|
||||||
cmp -s $decout $0 || die "decrypted file mismatch with $0"
|
|
||||||
|
|
||||||
# now with sender verification
|
|
||||||
$bin e -E FOO --overwrite -o $encout -s $sk $pk2 $0 || die "can't sender-encrypt to $pk2"
|
|
||||||
$bin d -E FOO --overwrite -o $decout -v $pk $sk2 $encout || die "can't decrypt with $sk2"
|
|
||||||
cmp -s $decout $0 || die "decrypted file mismatch with $0"
|
|
||||||
|
|
||||||
# Only delete if everything worked
|
|
||||||
echo "$Z: All tests pass!"
|
|
||||||
rm -rf $tmpdir
|
|
||||||
|
|
||||||
# vim: tw=100 sw=4 ts=4 expandtab
|
|
1
version
Normal file
1
version
Normal file
|
@ -0,0 +1 @@
|
||||||
|
0.5.0
|
Loading…
Add table
Reference in a new issue