Compare commits
No commits in common. "master" and "v0.8.0" have entirely different histories.
34 changed files with 2444 additions and 3751 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -24,11 +24,12 @@ _testmain.go
|
|||
*.prof
|
||||
vendor/*
|
||||
|
||||
# vendor management
|
||||
vendor/src/*
|
||||
vendor/pkg/*
|
||||
bin/*
|
||||
sigtool
|
||||
|
||||
.??*.sw?
|
||||
*.pub
|
||||
*.key
|
||||
*.sig
|
||||
releases/*
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
[tools]
|
||||
golang = "1.24"
|
168
README.md
168
README.md
|
@ -7,40 +7,28 @@
|
|||
`sigtool` is an opinionated tool to generate keys, sign, verify, encrypt &
|
||||
decrypt files using Ed25519 signature scheme. In many ways, it is like
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
*or* the keys generated by sigtool. This means, you can send encrypted
|
||||
files to any recipient identified by their comment in `~/.ssh/authorized_keys`.
|
||||
|
||||
## How do I build it?
|
||||
You need two things:
|
||||
With Go 1.5 and later:
|
||||
|
||||
1. Protobuf compiler:
|
||||
|
||||
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
|
||||
git clone https://github.com/opencoff/sigtool
|
||||
cd sigtool
|
||||
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
|
||||
|
||||
|
||||
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
|
||||
keys.
|
||||
|
||||
|
@ -154,49 +138,27 @@ recipient can decrypt using their private key.
|
|||
|
||||
## Technical Details
|
||||
|
||||
### How is the file encryption done?
|
||||
### 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.
|
||||
|
||||
### How is the Encryption done?
|
||||
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
|
||||
HKDF-SHA256 into:
|
||||
a random 32-byte AES-256 key. 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
|
||||
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)
|
||||
- AES Nonce (12 bytes)
|
||||
- HMAC-SHA-256 key (32 bytes)
|
||||
### What is the public-key cryptography?
|
||||
`sigtool` uses Curve25519 ECC to generate shared secrets between
|
||||
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
|
||||
a context string.
|
||||
|
||||
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.
|
||||
The Ed25519 keys generated by `sigtool` are transformed to their
|
||||
corresponding Curve25519 points in order to generate the shared secret.
|
||||
This elliptic co-ordinate transform follows [FiloSottile's writeup][2].
|
||||
|
||||
### Format of the Encrypted File
|
||||
|
@ -219,18 +181,14 @@ described as a protobuf file (sign/hdr.proto):
|
|||
message header {
|
||||
uint32 chunk_size = 1;
|
||||
bytes salt = 2;
|
||||
bytes pk = 3; // sender's ephemeral curve PK
|
||||
bytes sender = 4; // ed25519 signature of key material
|
||||
repeated wrapped_key keys = 5;
|
||||
repeated wrapped_key keys = 3;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
bytes nonce = 2;
|
||||
bytes pk_hash = 1; // hash of Ed25519 PK
|
||||
bytes pk = 2; // curve25519 PK
|
||||
bytes nonce = 3; // AEAD nonce
|
||||
bytes key = 4; // AEAD encrypted key
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -241,49 +199,22 @@ chunk is encoded the same way:
|
|||
|
||||
```C
|
||||
4 byte chunk length (big endian encoding)
|
||||
AEAD encrypted chunk data
|
||||
encrypted chunk data
|
||||
AEAD tag
|
||||
```
|
||||
|
||||
The chunk length does _not_ include the AEAD tag length; it is implicitly
|
||||
computed. 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.
|
||||
|
||||
### 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
|
||||
The core logic is in `src/sign`: it is a library that exposes all the
|
||||
functionality: key generation, key parsing, signing, encryption, decryption
|
||||
etc.
|
||||
|
||||
* `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/sign` is a library to generate, verify and store Ed25519 keys
|
||||
and signatures. It uses the extended library (golang.org/x/crypto)
|
||||
for the underlying operations.
|
||||
|
||||
`src/crypt.go` contains the encryption & decryption code.
|
||||
|
||||
The generated keys and signatures are proper YAML files and human
|
||||
readable.
|
||||
|
@ -296,11 +227,6 @@ Signatures on large files are calculated efficiently by reading them
|
|||
in memory mapped mode (```mmap(2)```) and hashing the file contents
|
||||
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
|
||||
|
||||
### Ed25519 Public Key
|
||||
|
@ -321,6 +247,24 @@ And, a serialized Ed25519 private key looks like so:
|
|||
p: 1
|
||||
```
|
||||
|
||||
The Ed25519 private key is encrypted using AES-256-GCM AEAD mode;
|
||||
the encryption key is derived from the user supplied passphrase
|
||||
using scrypt KDF. A user supplied passphrase is first expanded
|
||||
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)
|
||||
key = Scrypt(hpass, 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
|
||||
|
||||
### Ed25519 Signature
|
||||
A generated signature looks like below after serialization:
|
||||
|
|
267
build
267
build
|
@ -13,13 +13,12 @@
|
|||
#
|
||||
# License: GPLv2
|
||||
#
|
||||
Progs="src:sigtool"
|
||||
Progs=".:sigtool"
|
||||
|
||||
# Relative path to protobuf sources
|
||||
# e.g. src/foo/a.proto
|
||||
Protobufs="internal/pb/hdr.proto"
|
||||
Protobufs="sign/hdr.proto"
|
||||
|
||||
#set -x
|
||||
|
||||
# -- DO NOT CHANGE ANYTHING AFTER THIS --
|
||||
|
||||
|
@ -28,11 +27,13 @@ PWD=`pwd`
|
|||
|
||||
Static=0
|
||||
Dryrun=0
|
||||
Prodver=""
|
||||
Repover=""
|
||||
Prodver=0.1
|
||||
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() {
|
||||
echo "$Z: $@" 1>&2
|
||||
|
@ -50,56 +51,47 @@ case $BASH_VERSION in
|
|||
;;
|
||||
esac
|
||||
|
||||
getvcs_version() {
|
||||
local rev=
|
||||
local prodv=
|
||||
local git=`which git`
|
||||
local hg=`which hg`
|
||||
|
||||
if [ -n "$git" ]; then
|
||||
local xrev=$(git describe --always --dirty --long --abbrev=12) || exit 1
|
||||
rev="git:$xrev"
|
||||
prodv=$(git tag --list | sort -V | tail -1)
|
||||
elif [ -n "$hg" ]; then
|
||||
local xrev=$(hg id --id) || exit 1
|
||||
local brev=${xrev%+}
|
||||
if [ "$brev" != "$xrev" ]; then
|
||||
rev="hg:${brev}-dirty"
|
||||
else
|
||||
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"
|
||||
# build a tool that runs on the host - if needed.
|
||||
hosttool() {
|
||||
local tool=$1
|
||||
local bindir=$2
|
||||
local src=$3
|
||||
|
||||
local p=$(type -P $tool)
|
||||
if [ -n "$p" ]; then
|
||||
echo $p
|
||||
return 0
|
||||
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
|
||||
}
|
||||
|
||||
read -r Repover Prodver <<< $(getvcs_version)
|
||||
|
||||
|
||||
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
|
||||
$0 - A Go production build tool that adds git-repository information,
|
||||
product version, build-timestamp etc. It supports cross-compilation,
|
||||
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.
|
||||
|
||||
Usage: $0
|
||||
|
@ -108,14 +100,12 @@ Usage: $0
|
|||
Where OS-ARCH denotes one of the valid OS, ARCH combinations supported by 'go'.
|
||||
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
|
||||
built. The current repository version is $Repover.
|
||||
If ./version is present, its content are used as version number for the binary.
|
||||
|
||||
Options:
|
||||
-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]
|
||||
-V N, --version=N Use 'N' as the product version string [$Prodver]
|
||||
-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]
|
||||
-v, --verbose Build verbosely (adds "-v" to go tooling) [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]
|
||||
--print-arch Print the target architecture and exit
|
||||
EOF
|
||||
|
||||
exit 0
|
||||
}
|
||||
|
||||
host=`uname|tr '[A-Z]' '[a-z]'`
|
||||
export GO15VENDOREXPERIMENT=1
|
||||
|
||||
declare -A oses
|
||||
declare -A cpus
|
||||
|
@ -165,7 +153,6 @@ done
|
|||
Tool=
|
||||
doinit=0
|
||||
args=
|
||||
Printarch=0
|
||||
|
||||
#set -x
|
||||
ac_prev=
|
||||
|
@ -193,23 +180,13 @@ do
|
|||
--arch=*)
|
||||
Arch=$ac_optarg
|
||||
;;
|
||||
|
||||
-a|--arch)
|
||||
ac_prev=Arch
|
||||
;;
|
||||
|
||||
-b|--bindir)
|
||||
ac_prev=Bindir
|
||||
;;
|
||||
|
||||
--bindir=*)
|
||||
Bindir=$ac_optarg
|
||||
;;
|
||||
|
||||
--version=*)
|
||||
Prodver=$ac_optarg
|
||||
;;
|
||||
|
||||
--test|-t)
|
||||
Tool=test
|
||||
;;
|
||||
|
@ -218,14 +195,9 @@ do
|
|||
Tool=vet
|
||||
;;
|
||||
|
||||
--mod)
|
||||
Tool=mod
|
||||
;;
|
||||
|
||||
-V|--version)
|
||||
ac_prev=Prodver
|
||||
;;
|
||||
|
||||
-v|--verbose)
|
||||
Verbose=1
|
||||
;;
|
||||
|
@ -242,14 +214,6 @@ do
|
|||
set -x
|
||||
;;
|
||||
|
||||
--go-root=*)
|
||||
GoRoot=$ac_optarg
|
||||
;;
|
||||
|
||||
--print-arch)
|
||||
Printarch=1
|
||||
;;
|
||||
|
||||
*) # first non option terminates option processing.
|
||||
# we gather all remaining args and bundle them up.
|
||||
args="$args $ac_option"
|
||||
|
@ -261,80 +225,10 @@ do
|
|||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
[ $Dryrun -gt 0 ] && e=echo
|
||||
|
||||
# let every error abort
|
||||
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
|
||||
#set -e
|
||||
|
||||
# This fragment can't be in a function - since it exports several vars
|
||||
if [ -n "$Arch" ]; then
|
||||
|
@ -380,32 +274,63 @@ else
|
|||
fi
|
||||
fi
|
||||
|
||||
if [ $Printarch -gt 0 ]; then
|
||||
echo "$hostos-$hostcpu"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
|
||||
# This is where build outputs go
|
||||
Outdir=$Bindir/$cross
|
||||
Hostbindir=$Bindir/$hostos-$hostcpu
|
||||
export PATH=$Hostbindir:$PATH
|
||||
Bindir=$PWD/bin/$cross
|
||||
Hostbindir=$PWD/bin/$hostos-$hostcpu
|
||||
|
||||
[ -d $Outdir ] || mkdir -p $Outdir
|
||||
[ -d $Bindir ] || mkdir -p $Bindir
|
||||
[ -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
|
||||
if [ -n "$Protobufs" ]; then
|
||||
set +e
|
||||
buildproto $Protobufs
|
||||
set -e
|
||||
slick=$Hostbindir/protoc-gen-gogoslick
|
||||
slicksrc=github.com/gogo/protobuf/protoc-gen-gogoslick
|
||||
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
|
||||
|
||||
# Get git/hg version info for the build
|
||||
repover="main.RepoVersion=$Repover"
|
||||
repover="main.RepoVersion=$rev"
|
||||
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=""
|
||||
|
||||
[ $Verbose -gt 0 ] && vflag="-v"
|
||||
|
@ -413,17 +338,12 @@ vflag=""
|
|||
case $Tool in
|
||||
test)
|
||||
set -- $args
|
||||
$e $Go test $vflag "$@"
|
||||
$e go test $vflag "$@"
|
||||
;;
|
||||
|
||||
vet)
|
||||
set -- $args
|
||||
$e $Go vet $vflag "$@"
|
||||
;;
|
||||
|
||||
mod)
|
||||
set -- $args
|
||||
$e $Go mod $vflag "$@"
|
||||
$e go vet $vflag "$@"
|
||||
;;
|
||||
|
||||
*) # Default is to build programs
|
||||
|
@ -434,9 +354,7 @@ case $Tool in
|
|||
all="$@"
|
||||
fi
|
||||
|
||||
[ -z "$all" ] && die "No programs specified. Try '$Z --help'"
|
||||
|
||||
echo "Building $Prodver ($Repover), $cross $msg .."
|
||||
echo "Building $msg $Prodver ($rev) for $cross .."
|
||||
|
||||
for p in $all; do
|
||||
if echo $p | grep -q ':' ; then
|
||||
|
@ -446,15 +364,8 @@ case $Tool in
|
|||
out=$p
|
||||
dir=$p
|
||||
fi
|
||||
|
||||
# Add .exe suffix to out if needed
|
||||
if [ "$GOOS" = "windows" ]; then
|
||||
base=${out%%.exe}
|
||||
out="${base}.exe"
|
||||
fi
|
||||
|
||||
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
|
||||
;;
|
||||
esac
|
||||
|
|
|
@ -20,10 +20,9 @@ import (
|
|||
"os"
|
||||
"strings"
|
||||
|
||||
"git.rgst.io/homelab/sigtool/v3/sign"
|
||||
"github.com/opencoff/go-fio"
|
||||
"github.com/opencoff/go-utils"
|
||||
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]
|
||||
|
@ -36,25 +35,19 @@ func encrypt(args []string) {
|
|||
|
||||
var outfile string
|
||||
var keyfile string
|
||||
var szstr string = "128k"
|
||||
var envpw string
|
||||
var nopw, force bool
|
||||
var nopw bool
|
||||
var blksize uint64
|
||||
|
||||
fs.StringVarP(&outfile, "outfile", "o", "", "Write the output to file `F`")
|
||||
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.StringVarP(&envpw, "env-password", "E", "", "Use passphrase from environment variable `E`")
|
||||
fs.StringVarP(&szstr, "block-size", "B", szstr, "Use `S` as the encryption block size")
|
||||
fs.BoolVarP(&force, "overwrite", "", false, "Overwrite the output file if it exists")
|
||||
fs.StringVarP(&envpw, "env-password", "", "", "Use passphrase from environment variable `E`")
|
||||
fs.SizeVarP(&blksize, "block-size", "B", 128 * 1024, "Use `S` as the encryption block size")
|
||||
|
||||
err := fs.Parse(args)
|
||||
if err != nil {
|
||||
Die("%s", err)
|
||||
}
|
||||
|
||||
if blksize, err = utils.ParseSize(szstr); err != nil {
|
||||
Die("%s", err)
|
||||
die("%s", err)
|
||||
}
|
||||
|
||||
var pws, infile string
|
||||
|
@ -70,23 +63,23 @@ func encrypt(args []string) {
|
|||
} else {
|
||||
pws, err = utils.Askpass("Enter passphrase for private key", false)
|
||||
if err != nil {
|
||||
Die("%s", err)
|
||||
die("%s", err)
|
||||
}
|
||||
}
|
||||
return []byte(pws), nil
|
||||
})
|
||||
if err != nil {
|
||||
Die("%s", err)
|
||||
die("%s", err)
|
||||
}
|
||||
}
|
||||
|
||||
args = fs.Args()
|
||||
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 outfd io.WriteCloser = os.Stdout
|
||||
var outfd io.Writer = os.Stdout
|
||||
var inf *os.File
|
||||
|
||||
if len(args) > 1 {
|
||||
|
@ -102,14 +95,14 @@ func encrypt(args []string) {
|
|||
// Lets try to read the authorized files
|
||||
home, err := os.UserHomeDir()
|
||||
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)
|
||||
authdata, err := ioutil.ReadFile(authkeys)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
Die("can't open %s: %s", authkeys, err)
|
||||
if err != os.ErrNotExist {
|
||||
die("can't open %s: %s", authkeys, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -121,43 +114,31 @@ func encrypt(args []string) {
|
|||
}
|
||||
|
||||
if len(outfile) > 0 && outfile != "-" {
|
||||
var mode os.FileMode = 0600 // conservative output mode
|
||||
|
||||
if inf != nil {
|
||||
var err error
|
||||
var ist, ost os.FileInfo
|
||||
|
||||
if ost, err = os.Stat(outfile); err != nil {
|
||||
Die("can't stat %s: %s", outfile, err)
|
||||
ost, err := os.Stat(outfile)
|
||||
if err != nil {
|
||||
die("can't stat %s: %s", outfile, err)
|
||||
}
|
||||
|
||||
if ist, err = inf.Stat(); err != nil {
|
||||
Die("can't stat %s: %s", infile, err)
|
||||
ist, err := inf.Stat()
|
||||
if err != nil {
|
||||
die("can't stat %s: %s", infile, err)
|
||||
}
|
||||
|
||||
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
|
||||
if force {
|
||||
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)
|
||||
}
|
||||
outf := mustOpen(outfile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC)
|
||||
defer outf.Close()
|
||||
|
||||
AtExit(sf.Abort)
|
||||
defer sf.Abort()
|
||||
outfd = sf
|
||||
outfd = outf
|
||||
}
|
||||
|
||||
en, err := sign.NewEncryptor(sk, blksize)
|
||||
if err != nil {
|
||||
Die("%s", err)
|
||||
die("%s", err)
|
||||
}
|
||||
|
||||
errs := 0
|
||||
|
@ -170,14 +151,14 @@ func encrypt(args []string) {
|
|||
var ok bool
|
||||
pk, ok = keymap[fn]
|
||||
if !ok {
|
||||
Warn("can't find user %s in %s", fn, authkeys)
|
||||
warn("can't find user %s in %s", fn, authkeys)
|
||||
errs += 1
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
pk, err = sign.ReadPublicKey(fn)
|
||||
if err != nil {
|
||||
Warn("%s", err)
|
||||
warn("%s", err)
|
||||
errs += 1
|
||||
continue
|
||||
}
|
||||
|
@ -185,19 +166,18 @@ func encrypt(args []string) {
|
|||
|
||||
err = en.AddRecipient(pk)
|
||||
if err != nil {
|
||||
Die("%s", err)
|
||||
die("%s", err)
|
||||
}
|
||||
}
|
||||
|
||||
if errs > 0 {
|
||||
Die("Too many errors!")
|
||||
die("Too many errors!")
|
||||
}
|
||||
|
||||
err = en.Encrypt(infd, outfd)
|
||||
if err != nil {
|
||||
Die("%s", err)
|
||||
die("%s", err)
|
||||
}
|
||||
outfd.Close()
|
||||
}
|
||||
|
||||
type nullWriter struct{}
|
||||
|
@ -222,27 +202,26 @@ func decrypt(args []string) {
|
|||
var envpw string
|
||||
var outfile string
|
||||
var pubkey string
|
||||
var nopw, test, force bool
|
||||
var nopw, test bool
|
||||
|
||||
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.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.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)
|
||||
if err != nil {
|
||||
Die("%s", err)
|
||||
die("%s", err)
|
||||
}
|
||||
|
||||
args = fs.Args()
|
||||
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 outfd io.WriteCloser = os.Stdout
|
||||
var outfd io.Writer = os.Stdout
|
||||
var inf *os.File
|
||||
var infile string
|
||||
|
||||
|
@ -258,13 +237,13 @@ func decrypt(args []string) {
|
|||
} else {
|
||||
pws, err = utils.Askpass("Enter passphrase for private key", false)
|
||||
if err != nil {
|
||||
Die("%s", err)
|
||||
die("%s", err)
|
||||
}
|
||||
}
|
||||
return []byte(pws), nil
|
||||
})
|
||||
if err != nil {
|
||||
Die("%s", err)
|
||||
die("%s", err)
|
||||
}
|
||||
|
||||
var pk *sign.PublicKey
|
||||
|
@ -272,7 +251,7 @@ func decrypt(args []string) {
|
|||
if len(pubkey) > 0 {
|
||||
pk, err = sign.ReadPublicKey(pubkey)
|
||||
if err != nil {
|
||||
Die("%s", err)
|
||||
die("%s", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -289,66 +268,44 @@ func decrypt(args []string) {
|
|||
if test {
|
||||
outfd = &nullWriter{}
|
||||
} else if len(outfile) > 0 && outfile != "-" {
|
||||
var mode os.FileMode = 0600 // conservative mode
|
||||
|
||||
if inf != nil {
|
||||
var ist, ost os.FileInfo
|
||||
var err error
|
||||
|
||||
if ost, err = os.Stat(outfile); err != nil {
|
||||
Die("can't stat %s: %s", outfile, err)
|
||||
ost, err := os.Stat(outfile)
|
||||
if err != nil {
|
||||
die("can't stat %s: %s", outfile, err)
|
||||
}
|
||||
if ist, err = inf.Stat(); err != nil {
|
||||
Die("can't stat %s: %s", infile, err)
|
||||
ist, err := inf.Stat()
|
||||
if err != nil {
|
||||
die("can't stat %s: %s", infile, err)
|
||||
}
|
||||
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
|
||||
if force {
|
||||
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)
|
||||
}
|
||||
outf := mustOpen(outfile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC)
|
||||
defer outf.Close()
|
||||
|
||||
AtExit(sf.Abort)
|
||||
defer sf.Abort()
|
||||
outfd = sf
|
||||
outfd = outf
|
||||
}
|
||||
|
||||
d, err := sign.NewDecryptor(infd)
|
||||
if err != nil {
|
||||
Die("%s", err)
|
||||
die("%s", err)
|
||||
}
|
||||
|
||||
err = d.SetPrivateKey(sk, pk)
|
||||
if err != nil {
|
||||
Die("%s", err)
|
||||
die("%s", err)
|
||||
}
|
||||
|
||||
if pk == nil && d.AuthenticatedSender() {
|
||||
var fn string = infile
|
||||
if len(fn) == 0 || fn == "-" {
|
||||
fn = "<stdin>"
|
||||
err = d.Decrypt(outfd)
|
||||
if err != nil {
|
||||
die("%s", err)
|
||||
}
|
||||
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")
|
||||
warn("Enc file OK")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func encryptUsage(fs *flag.FlagSet) {
|
||||
|
@ -356,16 +313,9 @@ func encryptUsage(fs *flag.FlagSet) {
|
|||
|
||||
Usage: %s encrypt [options] to [to ...] infile|-
|
||||
|
||||
Where TO is the public key of the recipient; it can be one of:
|
||||
|
||||
- a file referring to an SSH or sigtool public key.
|
||||
- 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.
|
||||
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,
|
||||
%s writes the encrypted output to STDOUT.
|
||||
|
||||
Options:
|
||||
`, Z, Z, Z, Z)
|
||||
|
@ -393,7 +343,7 @@ Options:
|
|||
func mustOpen(fn string, flag int) *os.File {
|
||||
fdk, err := os.OpenFile(fn, flag, 0600)
|
||||
if err != nil {
|
||||
Die("can't open file %s: %s", fn, err)
|
||||
die("can't open file %s: %s", fn, err)
|
||||
}
|
||||
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 (
|
||||
github.com/dchest/bcrypt_pbkdf v0.0.0-20150205184540-83f37f9c154a
|
||||
github.com/opencoff/go-fio v0.5.14
|
||||
github.com/opencoff/go-mmap v0.1.5
|
||||
github.com/opencoff/go-utils v1.0.2
|
||||
github.com/opencoff/pflag v1.0.7
|
||||
github.com/planetscale/vtprotobuf v0.6.0
|
||||
golang.org/x/crypto v0.36.0
|
||||
google.golang.org/protobuf v1.36.5
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
github.com/gogo/protobuf v1.3.1
|
||||
github.com/opencoff/go-utils v0.4.1
|
||||
github.com/opencoff/pflag v0.5.0
|
||||
golang.org/x/crypto v0.0.0-20200109152110-61a87790db17
|
||||
gopkg.in/yaml.v2 v2.2.7
|
||||
)
|
||||
|
||||
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
|
||||
|
|
56
go.sum
56
go.sum
|
@ -1,39 +1,23 @@
|
|||
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/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/opencoff/go-fio v0.5.14 h1:PGi4XLLO4RSuc3m5exY0G2vweov6w3UThhScehBfM8c=
|
||||
github.com/opencoff/go-fio v0.5.14/go.mod h1:hoSySYpavRnfQUsxzUgadk31kYiNQhMDvA2MObsXKf8=
|
||||
github.com/opencoff/go-mmap v0.1.5 h1:RKPtevC4mOW5bi9skBPPo4nFTIH4lVWAL20Tff+FjLg=
|
||||
github.com/opencoff/go-mmap v0.1.5/go.mod h1:y/6Jk/tDUc00k3oSQpiJX++20Nw7xFSlc5kLkhGnRXw=
|
||||
github.com/opencoff/go-utils v1.0.2 h1:BANRL8ZxgHpuo8gQBAzT3M9Im3aNFhaWW28jhc86LNs=
|
||||
github.com/opencoff/go-utils v1.0.2/go.mod h1:eZkEVQVzNfuE8uGepyhscMsqcXq7liGbBHYYwgYaoy8=
|
||||
github.com/opencoff/pflag v1.0.7 h1:o5cQIuX75bDcdJ6AXl68gzpA72a3CJ2MPStaMnEuwi4=
|
||||
github.com/opencoff/pflag v1.0.7/go.mod h1:2bXtpAD/5h/2LarkbsRwiUxqnvB1nZBzn9Xjad1P41A=
|
||||
github.com/pkg/xattr v0.4.10 h1:Qe0mtiNFHQZ296vRgUjRCoPHPqH7VdTOrZx3g0T+pGA=
|
||||
github.com/pkg/xattr v0.4.10/go.mod h1:di8WF84zAKk8jzR1UBTEWh9AUlIZZ7M/JNt8e9B6ktU=
|
||||
github.com/planetscale/vtprotobuf v0.6.0 h1:nBeETjudeJ5ZgBHUz1fVHvbqUKnYOXNhsIEabROxmNA=
|
||||
github.com/planetscale/vtprotobuf v0.6.0/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
|
||||
github.com/puzpuzpuz/xsync/v3 v3.5.1 h1:GJYJZwO6IdxN/IKbneznS6yPkVC+c3zyY/j19c++5Fg=
|
||||
github.com/puzpuzpuz/xsync/v3 v3.5.1/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=
|
||||
golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs=
|
||||
golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
|
||||
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
||||
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
||||
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=
|
||||
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
|
||||
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/opencoff/go-utils v0.4.1 h1:Ke4Q1Tl2GKMI+dwleuPNHH713ngRiNMOFIkymncHqXg=
|
||||
github.com/opencoff/go-utils v0.4.1/go.mod h1:c+7QUAiCCHcNH6OGvsZ0fviG7cgse8Y3ucg+xy7sGXM=
|
||||
github.com/opencoff/pflag v0.5.0 h1:kK3cSTlGj0fHby/PoFzHkf+Jx3PdiACJwzYDWEWlEKQ=
|
||||
github.com/opencoff/pflag v0.5.0/go.mod h1:mTLzGGUGda1Av3d34iAJlh0JIlRxmFZtmc6qoWPspK0=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200109152110-61a87790db17 h1:nVJ3guKA9qdkEQ3TUdXI9QSINo2CUPM/cySEvw2w8I0=
|
||||
golang.org/x/crypto v0.0.0-20200109152110-61a87790db17/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
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.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=
|
||||
gopkg.in/yaml.v2 v2.2.7/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
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
GPL v2.0
|
||||
|
|
838
sign/encrypt.go
838
sign/encrypt.go
File diff suppressed because it is too large
Load diff
|
@ -22,22 +22,12 @@ import (
|
|||
"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()
|
||||
receiver, err := NewKeypair()
|
||||
assert(err == nil, "receiver keypair gen failed: %s", err)
|
||||
|
||||
var blkSize int = 1024
|
||||
var size int = (blkSize * 10)
|
||||
|
@ -51,11 +41,11 @@ func TestEncryptSimple(t *testing.T) {
|
|||
ee, err := NewEncryptor(nil, uint64(blkSize))
|
||||
assert(err == nil, "encryptor create fail: %s", err)
|
||||
|
||||
err = ee.AddRecipient(pk)
|
||||
err = ee.AddRecipient(&receiver.Pub)
|
||||
assert(err == nil, "can't add recipient: %s", err)
|
||||
|
||||
rd := bytes.NewBuffer(buf)
|
||||
wr := Buffer{}
|
||||
wr := bytes.Buffer{}
|
||||
|
||||
err = ee.Encrypt(rd, &wr)
|
||||
assert(err == nil, "encrypt fail: %s", err)
|
||||
|
@ -65,10 +55,10 @@ func TestEncryptSimple(t *testing.T) {
|
|||
dd, err := NewDecryptor(rd)
|
||||
assert(err == nil, "decryptor create fail: %s", err)
|
||||
|
||||
err = dd.SetPrivateKey(sk, nil)
|
||||
err = dd.SetPrivateKey(&receiver.Sec, nil)
|
||||
assert(err == nil, "decryptor can't add SK: %s", err)
|
||||
|
||||
wr = Buffer{}
|
||||
wr = bytes.Buffer{}
|
||||
err = dd.Decrypt(&wr)
|
||||
assert(err == nil, "decrypt fail: %s", err)
|
||||
|
||||
|
@ -78,67 +68,12 @@ func TestEncryptSimple(t *testing.T) {
|
|||
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()
|
||||
receiver, err := NewKeypair()
|
||||
assert(err == nil, "receiver keypair gen failed: %s", err)
|
||||
|
||||
var blkSize int = 1024
|
||||
var size int = (blkSize * 23) + randmod(blkSize)
|
||||
|
@ -152,11 +87,11 @@ func TestEncryptCorrupted(t *testing.T) {
|
|||
ee, err := NewEncryptor(nil, uint64(blkSize))
|
||||
assert(err == nil, "encryptor create fail: %s", err)
|
||||
|
||||
err = ee.AddRecipient(pk)
|
||||
err = ee.AddRecipient(&receiver.Pub)
|
||||
assert(err == nil, "can't add recipient: %s", err)
|
||||
|
||||
rd := bytes.NewReader(buf)
|
||||
wr := Buffer{}
|
||||
wr := bytes.Buffer{}
|
||||
|
||||
err = ee.Encrypt(rd, &wr)
|
||||
assert(err == nil, "encrypt fail: %s", err)
|
||||
|
@ -164,7 +99,6 @@ func TestEncryptCorrupted(t *testing.T) {
|
|||
rb := wr.Bytes()
|
||||
n := len(rb)
|
||||
|
||||
// corrupt the input
|
||||
for i := 0; i < n; i++ {
|
||||
j := randint() % n
|
||||
rb[j] = byte(randint() & 0xff)
|
||||
|
@ -180,11 +114,11 @@ func TestEncryptCorrupted(t *testing.T) {
|
|||
func TestEncryptSenderVerified(t *testing.T) {
|
||||
assert := newAsserter(t)
|
||||
|
||||
sender, err := NewPrivateKey()
|
||||
assert(err == nil, "sender SK gen failed: %s", err)
|
||||
sender, err := NewKeypair()
|
||||
assert(err == nil, "sender keypair gen failed: %s", err)
|
||||
|
||||
receiver, err := NewPrivateKey()
|
||||
assert(err == nil, "receiver SK gen failed: %s", err)
|
||||
receiver, err := NewKeypair()
|
||||
assert(err == nil, "receiver keypair gen failed: %s", err)
|
||||
|
||||
var blkSize int = 1024
|
||||
var size int = (blkSize * 23) + randmod(blkSize)
|
||||
|
@ -195,14 +129,14 @@ func TestEncryptSenderVerified(t *testing.T) {
|
|||
buf[i] = byte(i & 0xff)
|
||||
}
|
||||
|
||||
ee, err := NewEncryptor(sender, uint64(blkSize))
|
||||
ee, err := NewEncryptor(&sender.Sec, uint64(blkSize))
|
||||
assert(err == nil, "encryptor create fail: %s", err)
|
||||
|
||||
err = ee.AddRecipient(receiver.PublicKey())
|
||||
err = ee.AddRecipient(&receiver.Pub)
|
||||
assert(err == nil, "can't add recipient: %s", err)
|
||||
|
||||
rd := bytes.NewBuffer(buf)
|
||||
wr := Buffer{}
|
||||
wr := bytes.Buffer{}
|
||||
|
||||
err = ee.Encrypt(rd, &wr)
|
||||
assert(err == nil, "encrypt fail: %s", err)
|
||||
|
@ -212,18 +146,10 @@ func TestEncryptSenderVerified(t *testing.T) {
|
|||
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())
|
||||
err = dd.SetPrivateKey(&receiver.Sec, &sender.Pub)
|
||||
assert(err == nil, "decryptor can't add SK: %s", err)
|
||||
|
||||
wr = Buffer{}
|
||||
wr = bytes.Buffer{}
|
||||
err = dd.Decrypt(&wr)
|
||||
assert(err == nil, "decrypt fail: %s", err)
|
||||
|
||||
|
@ -237,8 +163,8 @@ func TestEncryptSenderVerified(t *testing.T) {
|
|||
func TestEncryptMultiReceiver(t *testing.T) {
|
||||
assert := newAsserter(t)
|
||||
|
||||
sender, err := NewPrivateKey()
|
||||
assert(err == nil, "sender SK gen failed: %s", err)
|
||||
sender, err := NewKeypair()
|
||||
assert(err == nil, "sender keypair gen failed: %s", err)
|
||||
|
||||
var blkSize int = 1024
|
||||
var size int = (blkSize * 23) + randmod(blkSize)
|
||||
|
@ -249,22 +175,22 @@ func TestEncryptMultiReceiver(t *testing.T) {
|
|||
buf[i] = byte(i & 0xff)
|
||||
}
|
||||
|
||||
ee, err := NewEncryptor(sender, uint64(blkSize))
|
||||
ee, err := NewEncryptor(&sender.Sec, uint64(blkSize))
|
||||
assert(err == nil, "encryptor create fail: %s", err)
|
||||
|
||||
n := 4
|
||||
rx := make([]*PrivateKey, n)
|
||||
rx := make([]*Keypair, n)
|
||||
for i := 0; i < n; i++ {
|
||||
r, err := NewPrivateKey()
|
||||
assert(err == nil, "can't make receiver SK %d: %s", i, err)
|
||||
r, err := NewKeypair()
|
||||
assert(err == nil, "can't make receiver key %d: %s", i, err)
|
||||
rx[i] = r
|
||||
|
||||
err = ee.AddRecipient(r.PublicKey())
|
||||
err = ee.AddRecipient(&r.Pub)
|
||||
assert(err == nil, "can't add recipient %d: %s", i, err)
|
||||
}
|
||||
|
||||
rd := bytes.NewBuffer(buf)
|
||||
wr := Buffer{}
|
||||
wr := bytes.Buffer{}
|
||||
|
||||
err = ee.Encrypt(rd, &wr)
|
||||
assert(err == nil, "encrypt fail: %s", err)
|
||||
|
@ -276,10 +202,10 @@ func TestEncryptMultiReceiver(t *testing.T) {
|
|||
dd, err := NewDecryptor(rd)
|
||||
assert(err == nil, "decryptor %d create fail: %s", i, err)
|
||||
|
||||
err = dd.SetPrivateKey(rx[i], sender.PublicKey())
|
||||
err = dd.SetPrivateKey(&rx[i].Sec, &sender.Pub)
|
||||
assert(err == nil, "decryptor can't add SK %d: %s", i, err)
|
||||
|
||||
wr = Buffer{}
|
||||
wr = bytes.Buffer{}
|
||||
err = dd.Decrypt(&wr)
|
||||
assert(err == nil, "decrypt %d fail: %s", i, err)
|
||||
|
||||
|
@ -290,175 +216,6 @@ func TestEncryptMultiReceiver(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
500
sign/sign.go
500
sign/sign.go
|
@ -18,37 +18,332 @@
|
|||
package sign
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"crypto/subtle"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"hash"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
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
|
||||
type Signature struct {
|
||||
Sig []byte // Ed25519 sig bytes
|
||||
pkhash []byte // [0:16] SHA256 hash of public key needed for verification
|
||||
}
|
||||
|
||||
// Length of Ed25519 Public Key Hash
|
||||
const PKHashLength = 16
|
||||
|
||||
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"`
|
||||
}
|
||||
|
||||
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("make priv key: can't parse YAML: %s", err)
|
||||
}
|
||||
|
||||
b64 := base64.StdEncoding.DecodeString
|
||||
|
||||
salt, err := b64(ssk.Salt)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("make priv key: can't decode salt: %s", err)
|
||||
}
|
||||
|
||||
esk, err := b64(ssk.Esk)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("make priv key: can't decode key: %s", err)
|
||||
}
|
||||
|
||||
// We take short passwords and extend them
|
||||
pwb := sha512.Sum512(pw)
|
||||
|
||||
// "32" == Length of AES-256 key
|
||||
key, err := scrypt.Key(pwb[:], salt, ssk.N, ssk.R, ssk.P, 32)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("make priv key: can't derive key: %s", err)
|
||||
}
|
||||
|
||||
aes, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("make priv key: aes failure: %s", err)
|
||||
}
|
||||
|
||||
ae, err := cipher.NewGCM(aes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("make priv key: aes failure: %s", err)
|
||||
}
|
||||
|
||||
skb, err := ae.Open(nil, salt[:ae.NonceSize()], esk, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("make priv key: wrong password")
|
||||
}
|
||||
|
||||
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
|
||||
// AEAD encryption for protecting the private key
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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 fmt.Errorf("marshal: can't derive scrypt key: %s", err)
|
||||
}
|
||||
|
||||
aes, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return fmt.Errorf("marshal: %s", err)
|
||||
}
|
||||
|
||||
ae, err := cipher.NewGCM(aes)
|
||||
if err != nil {
|
||||
return 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.
|
||||
|
||||
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
|
||||
// Signature is an YAML file:
|
||||
//
|
||||
// Comment: source file path
|
||||
// Signature: Ed25519 signature
|
||||
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)
|
||||
|
||||
sig, err := x.Sign(rand.Reader, ck, crypto.Hash(0))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't sign %x: %s", ck, err)
|
||||
|
@ -88,13 +383,12 @@ func ReadSignature(fn string) (*Signature, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
var sig Signature
|
||||
return makeSignature(&sig, yml)
|
||||
return MakeSignature(yml)
|
||||
}
|
||||
|
||||
// Parse serialized signature from bytes 'b' and construct a
|
||||
// Signature object
|
||||
func makeSignature(sig *Signature, b []byte) (*Signature, error) {
|
||||
func MakeSignature(b []byte) (*Signature, error) {
|
||||
var ss signature
|
||||
err := yaml.Unmarshal(b, &ss)
|
||||
if err != nil {
|
||||
|
@ -113,33 +407,29 @@ func makeSignature(sig *Signature, b []byte) (*Signature, error) {
|
|||
return nil, fmt.Errorf("can't decode Base64:Pkhash <%s>: %s", ss.Pkhash, err)
|
||||
}
|
||||
|
||||
sig.Sig = s
|
||||
sig.pkhash = p
|
||||
return sig, nil
|
||||
return &Signature{Sig: s, pkhash: p}, nil
|
||||
}
|
||||
|
||||
// MarshalBinary marshals a signature into a byte stream with
|
||||
// an optional caller supplied comment.
|
||||
func (sig *Signature) MarshalBinary(comment string) ([]byte, error) {
|
||||
// Serialize a signature suitable for storing in durable media
|
||||
func (sig *Signature) Serialize(comment string) ([]byte, error) {
|
||||
|
||||
sigs := base64.StdEncoding.EncodeToString(sig.Sig)
|
||||
pks := base64.StdEncoding.EncodeToString(sig.pkhash)
|
||||
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)
|
||||
}
|
||||
|
||||
// UnmarshalBinary constructs a Signature from a previously
|
||||
// serialized bytestream
|
||||
func (sig *Signature) UnmarshalBinary(b []byte) error {
|
||||
_, err := makeSignature(sig, b)
|
||||
return err
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Serialize a signature suitable for storing in durable media
|
||||
func (sig *Signature) Serialize(fn, comment string, ovwrite bool) error {
|
||||
b, err := sig.MarshalBinary(comment)
|
||||
// SerializeFile serializes the signature to an output file 'f'
|
||||
func (sig *Signature) SerializeFile(fn, comment string) error {
|
||||
b, err := sig.Serialize(comment)
|
||||
if err == nil {
|
||||
err = writeFile(fn, b, ovwrite, 0644)
|
||||
err = writeFile(fn, b, 0644)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
@ -151,27 +441,171 @@ func (sig *Signature) IsPKMatch(pk *PublicKey) bool {
|
|||
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'
|
||||
// Return True if signature matches, False otherwise
|
||||
func (pk *PublicKey) VerifyFile(fn string, sig *Signature) (bool, error) {
|
||||
|
||||
ck, err := fileCksum(fn, sha512.New())
|
||||
if err != nil {
|
||||
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'
|
||||
// Return True if signature matches, False otherwise
|
||||
func (pk *PublicKey) VerifyMessage(ck []byte, sig *Signature) bool {
|
||||
h := sha512.New()
|
||||
h.Write([]byte("sigtool signed message"))
|
||||
h.Write(ck)
|
||||
ck = h.Sum(nil)[:]
|
||||
func (pk *PublicKey) VerifyMessage(ck []byte, sig *Signature) (bool, error) {
|
||||
|
||||
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:
|
||||
|
|
|
@ -28,7 +28,7 @@ func tempdir(t *testing.T) string {
|
|||
var b [10]byte
|
||||
|
||||
dn := os.TempDir()
|
||||
randRead(b[:])
|
||||
randread(b[:])
|
||||
|
||||
tmp := path.Join(dn, fmt.Sprintf("%x", b[:]))
|
||||
err := os.MkdirAll(tmp, 0755)
|
||||
|
@ -38,20 +38,16 @@ func tempdir(t *testing.T) string {
|
|||
return tmp
|
||||
}
|
||||
|
||||
var fixedPw = []byte("abc")
|
||||
var badPw = []byte("def")
|
||||
var nilPw []byte
|
||||
|
||||
// return a hardcoded password
|
||||
func hardcodedPw() ([]byte, error) {
|
||||
return fixedPw, nil
|
||||
return []byte("abc"), nil
|
||||
}
|
||||
|
||||
func wrongPw() ([]byte, error) {
|
||||
return badPw, nil
|
||||
return []byte("xyz"), nil
|
||||
}
|
||||
func emptyPw() ([]byte, error) {
|
||||
return nilPw, nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Return true if file exists, false otherwise
|
||||
|
@ -84,106 +80,112 @@ p: 1
|
|||
func TestSignSimple(t *testing.T) {
|
||||
assert := newAsserter(t)
|
||||
|
||||
sk, err := NewPrivateKey()
|
||||
assert(err == nil, "NewPrivateKey() fail")
|
||||
kp, err := NewKeypair()
|
||||
assert(err == nil, "NewKeyPair() fail")
|
||||
|
||||
pk := sk.PublicKey()
|
||||
|
||||
dn := t.TempDir()
|
||||
dn := tempdir(t)
|
||||
bn := fmt.Sprintf("%s/t0", dn)
|
||||
|
||||
err = kp.Serialize(bn, "", hardcodedPw)
|
||||
assert(err == nil, "keyPair.Serialize() fail")
|
||||
|
||||
pkf := fmt.Sprintf("%s.pub", 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
|
||||
assert(fileExists(pkf), "missing pkf %s", pkf)
|
||||
assert(fileExists(skf), "missing skf %s", skf)
|
||||
assert(fileExists(pkf), "missing pkf")
|
||||
assert(fileExists(skf), "missing skf")
|
||||
|
||||
npk, err := ReadPublicKey(pkf)
|
||||
pk, err := ReadPublicKey(pkf)
|
||||
assert(err == nil, "ReadPK() fail")
|
||||
|
||||
// send the public key as private key
|
||||
nsk, err := ReadPrivateKey(pkf, emptyPw)
|
||||
// -ditto- for Sk
|
||||
sk, err := ReadPrivateKey(pkf, emptyPw)
|
||||
assert(err != nil, "bad SK ReadSK fail: %s", err)
|
||||
|
||||
nsk, err = ReadPrivateKey(skf, emptyPw)
|
||||
assert(err != nil, "ReadSK() worked with empty pw")
|
||||
sk, err = ReadPrivateKey(skf, emptyPw)
|
||||
assert(err != nil, "ReadSK() empty pw fail: ks", err)
|
||||
|
||||
nsk, err = ReadPrivateKey(skf, wrongPw)
|
||||
assert(err != nil, "ReadSK() worked with wrong pw")
|
||||
sk, err = ReadPrivateKey(skf, wrongPw)
|
||||
assert(err != nil, "ReadSK() wrong pw fail: %s", err)
|
||||
|
||||
badf := fmt.Sprintf("%s/badf.key", dn)
|
||||
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)
|
||||
assert(err != nil, "decoded bad SK")
|
||||
sk, err = ReadPrivateKey(badf, hardcodedPw)
|
||||
assert(err != nil, "badsk read fail: %s", err)
|
||||
|
||||
// Finally, with correct password it should work.
|
||||
nsk, err = ReadPrivateKey(skf, hardcodedPw)
|
||||
assert(err == nil, "ReadSK() correct pw fail: %s", err)
|
||||
sk, err = ReadPrivateKey(skf, hardcodedPw)
|
||||
assert(err == nil, "ReadSK() correct pw fail")
|
||||
|
||||
// And, deserialized keys should be identical
|
||||
assert(byteEq(pk.Pk, npk.Pk), "pkbytes unequal")
|
||||
assert(byteEq(sk.Sk, nsk.Sk), "skbytes unequal")
|
||||
assert(byteEq(pk.Pk, kp.Pub.Pk), "pkbytes unequal")
|
||||
assert(byteEq(sk.Sk, kp.Sec.Sk), "skbytes unequal")
|
||||
|
||||
os.RemoveAll(dn)
|
||||
}
|
||||
|
||||
// #2. Create new key pair, sign a rand buffer and verify
|
||||
func TestSignRandBuf(t *testing.T) {
|
||||
assert := newAsserter(t)
|
||||
|
||||
sk, err := NewPrivateKey()
|
||||
assert(err == nil, "NewPrivateKey() fail: %s", err)
|
||||
kp, err := NewKeypair()
|
||||
assert(err == nil, "NewKeyPair() fail")
|
||||
|
||||
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[:], "")
|
||||
assert(err == nil, "sk.sign fail: %s", err)
|
||||
assert(err == nil, "sk.sign fail")
|
||||
assert(ss != nil, "sig is null")
|
||||
|
||||
// verify sig
|
||||
assert(ss.IsPKMatch(pk), "pk match fail")
|
||||
|
||||
// Corrupt the pkhash and see
|
||||
randRead(ss.pkhash)
|
||||
randread(ss.pkhash)
|
||||
assert(!ss.IsPKMatch(pk), "corrupt pk match fail")
|
||||
|
||||
// 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")
|
||||
|
||||
// proper checksum == should work
|
||||
ok = pk.VerifyMessage(ck[:], ss)
|
||||
ok, err = pk.VerifyMessage(ck[:], ss)
|
||||
assert(err == nil, "verify err")
|
||||
assert(ok, "verify fail")
|
||||
|
||||
// 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
|
||||
|
||||
zf := fmt.Sprintf("%s/file.dat", dn)
|
||||
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++ {
|
||||
randRead(buf[:])
|
||||
randread(buf[:])
|
||||
n, err := fd.Write(buf[:])
|
||||
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))
|
||||
|
@ -192,31 +194,27 @@ func TestSignRandBuf(t *testing.T) {
|
|||
fd.Close()
|
||||
|
||||
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")
|
||||
|
||||
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")
|
||||
|
||||
// Now, serialize the signature and read it back
|
||||
sf := fmt.Sprintf("%s/file.sig", dn)
|
||||
err = sig.Serialize(sf, "", true)
|
||||
assert(err == nil, "sig serialize fail: %s", err)
|
||||
|
||||
// now try to overwrite it
|
||||
err = sig.Serialize(sf, "", false)
|
||||
assert(err != nil, "sig serialize overwrote?!")
|
||||
err = sig.SerializeFile(sf, "")
|
||||
assert(err == nil, "sig serialize fail")
|
||||
|
||||
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(byteEq(s2.Sig, sig.Sig), "sig compare fail")
|
||||
|
||||
// If we give a wrong file, verify must fail
|
||||
st, err := os.Stat(zf)
|
||||
assert(err == nil, "file.dat stat fail: %s", err)
|
||||
assert(err == nil, "file.dat stat fail")
|
||||
|
||||
n := st.Size()
|
||||
assert(n == 8192*8, "file.dat size fail")
|
||||
|
@ -224,12 +222,12 @@ func TestSignRandBuf(t *testing.T) {
|
|||
os.Truncate(zf, n-1)
|
||||
|
||||
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")
|
||||
|
||||
// Now verify this corrupt file
|
||||
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")
|
||||
|
||||
os.RemoveAll(dn)
|
||||
|
@ -237,7 +235,7 @@ func TestSignRandBuf(t *testing.T) {
|
|||
|
||||
func Benchmark_Keygen(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _ = NewPrivateKey()
|
||||
_, _ = NewKeypair()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -254,8 +252,7 @@ func Benchmark_Sig(b *testing.B) {
|
|||
}
|
||||
|
||||
b.StopTimer()
|
||||
sk, _ := NewPrivateKey()
|
||||
pk := sk.PublicKey()
|
||||
kp, _ := NewKeypair()
|
||||
var sig *Signature
|
||||
for _, sz := range sizes {
|
||||
buf := randbuf(sz)
|
||||
|
@ -265,11 +262,11 @@ func Benchmark_Sig(b *testing.B) {
|
|||
b.ResetTimer()
|
||||
|
||||
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) {
|
||||
benchVerify(b, buf, sig, pk)
|
||||
benchVerify(b, buf, sig, &kp.Pub)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -289,7 +286,7 @@ func benchVerify(b *testing.B, buf []byte, sig *Signature, pk *PublicKey) {
|
|||
|
||||
func randbuf(sz uint) []byte {
|
||||
b := make([]byte, sz)
|
||||
randRead(b)
|
||||
randread(b)
|
||||
return b
|
||||
}
|
||||
|
||||
|
|
33
sign/ssh.go
33
sign/ssh.go
|
@ -25,8 +25,8 @@ import (
|
|||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/dchest/bcrypt_pbkdf"
|
||||
|
@ -34,6 +34,17 @@ import (
|
|||
"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
|
||||
|
||||
// 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) {
|
||||
splitter := regexp.MustCompile("[ \\t]+")
|
||||
v := splitter.Split(string(in), -1)
|
||||
v := bytes.Split(in, []byte(" \t"))
|
||||
if len(v) != 3 {
|
||||
return nil, ErrBadPublicKey
|
||||
}
|
||||
|
||||
return parseEncPubKey([]byte(v[1]), v[2])
|
||||
return parseEncPubKey(v[1], string(v[2]))
|
||||
}
|
||||
|
||||
// parse a wire encoded public key
|
||||
|
@ -95,13 +105,11 @@ func parseEncPubKey(in []byte, comm string) (*PublicKey, error) {
|
|||
return nil, ErrBadTrailers
|
||||
}
|
||||
|
||||
var pk PublicKey
|
||||
|
||||
if err = makePublicKeyFromBytes(&pk, w.KeyBytes); err == nil {
|
||||
pk, err := PublicKeyFromBytes(w.KeyBytes)
|
||||
if err == nil {
|
||||
pk.Comment = strings.TrimSpace(comm)
|
||||
return &pk, nil
|
||||
}
|
||||
return nil, err
|
||||
return pk, err
|
||||
}
|
||||
|
||||
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
|
||||
if err = makePrivateKeyFromBytes(&sk, key.Priv); err == nil {
|
||||
return &sk, nil
|
||||
}
|
||||
return nil, err
|
||||
pk, err := PrivateKeyFromBytes(key.Priv)
|
||||
return pk, err
|
||||
default:
|
||||
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
|
||||
}
|
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.8.0
|
Loading…
Add table
Reference in a new issue