-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
🌱 Initial commit of Pinki as a standalone binary
- Loading branch information
0 parents
commit 7f3532d
Showing
16 changed files
with
493 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
--- | ||
github: | ||
- canterberry |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
golang 1.17.6 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
Copyright 2022 Twuni | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy of | ||
this software and associated documentation files (the "Software"), to deal in | ||
the Software without restriction, including without limitation the rights to | ||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies | ||
of the Software, and to permit persons to whom the Software is furnished to do | ||
so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
# Pinki | ||
|
||
Pinki helps developers ship software with authenticity. | ||
|
||
Use it anywhere you would use `gpg` to sign and verify things. | ||
|
||
## Features | ||
|
||
* Easy to use | ||
* Portable, standalone binary | ||
* Anonymous -- a key is just a key, nothing more | ||
* Doesn't touch your filesystem | ||
* Reads and writes standard PEM-wrapped ASN.1 (compatible with X.509, GPG) | ||
|
||
## Installing | ||
|
||
### Precompiled Binaries | ||
|
||
Visit [Releases](https://releases.twuni.dev/pinki/latest/) to download a precompiled binary for your system. | ||
|
||
#### Verifying Binaries | ||
|
||
All releases are signed using the following [release signing key](https://releases.twuni.dev/verify.pem): | ||
|
||
```pem | ||
-----BEGIN PUBLIC KEY----- | ||
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEdDOWMNxI5f88Yck8WNcPsxDOwMbzoU/Y | ||
cZhfoR+gwGi0wRoSscWA1xy1BQTG6PNrQlvLJbfm2vAIAImnyMmhoKS3hwcO6F+5 | ||
4QjLZQJAQHZ6G7c842gYRSnwLLQ2GIvj | ||
-----END PUBLIC KEY----- | ||
``` | ||
|
||
Each release binary has a corresponding file with an **.asc** prefix containing the signature for that file. | ||
|
||
Here's an example of how you could use Pinki to verify itself. | ||
|
||
> :bulb: In practice, you probably want to use another tool to verify Pinki itself the first time you download | ||
> it. Once you have a genuine copy of `pinki`, then you can use it to verify updates to itself. | ||
```sh | ||
# Download Pinki for Linux (64-bit) | ||
$ curl -sSL -o pinki https://releases.twuni.dev/pinki/latest/linux-amd64/pinki | ||
|
||
# Use Pinki to verify itself | ||
$ ./pinki verify "$(curl -sSL https://releases.twuni.dev/verify.pem)" "$(curl -sSL https://releases.twuni.dev/pinki/latest/linux-amd64/pinki.asc)" < pinki | ||
``` | ||
|
||
If you get an output of `OK`, the signature is valid. | ||
|
||
### Building from source | ||
|
||
Already have `go`? Clone this repo and run `go build`. | ||
|
||
## Usage | ||
|
||
Pinki is designed to make it easy for you to do one of two things: | ||
|
||
* **Sign** your software so other people can verify its authenticity, or | ||
|
||
* **Verify** the authenticity of software you are using when the developers | ||
are using Pinki. | ||
|
||
### Signing your software with Pinki | ||
|
||
First, you'll need a private key. To create a new key with the | ||
recommended (default) options: | ||
|
||
```sh | ||
$ pinki key create | ||
-----BEGIN PRIVATE KEY----- | ||
............................................................... | ||
............................................................... | ||
............................................................... | ||
-----END PRIVATE KEY----- | ||
``` | ||
|
||
Save the output somewhere safe. Put it in your password manager, | ||
vault, or whatever you are using to keep sensitive information | ||
safe. | ||
|
||
Once you have a private key, you will need to *export* that in a | ||
way that is safe for people to verify your signatures: | ||
|
||
```sh | ||
$ pinki key export < /path/to/your-pinki-private-key | ||
-----BEGIN PUBLIC KEY----- | ||
............................................................... | ||
............................................................... | ||
-----END PUBLIC KEY----- | ||
``` | ||
|
||
Publish this public key somewhere that anyone you want to be able | ||
to verify your signatures is able to access it. You can commit it | ||
to your source code repo, publish it to your website, etc. | ||
|
||
> :bulb: The **public key** is not sensitive! You can safely share | ||
> it with anyone. | ||
Now that you have a private key, you're ready to sign your first thing! | ||
|
||
```sh | ||
$ pinki sign "$(cat /path/to/your-pinki-private-key)" < /path/to/your-thing-1.2.3.tar.gz | ||
-----BEGIN SIGNATURE----- | ||
............................................................... | ||
............................................................... | ||
-----END SIGNATURE----- | ||
``` | ||
|
||
Publish that signature any way you like. Conventionally, you might want to | ||
publish it as a file with the same name as the thing you've signed, but with | ||
a **.sig** suffix. So **foo-1.0.tgz** would have its signature in | ||
**foo-1.0.tgz.sig**. The choice is up to you. | ||
|
||
### Verifying a signature with Pinki | ||
|
||
To verify a signature, you'll need three things: | ||
|
||
* The thing that was signed (e.g: **foo-1.2.3.tgz**) | ||
* The signature (e.g: **foo-1.2.3.tgz.asc**) | ||
* The public key of the signer (e.g: **foomaker-signing-key.pem**) | ||
|
||
Check the release notes or installation/verification documentation of the | ||
thing you're trying to verify for more details on where to find these things. | ||
|
||
Once you have them, here's how you verify the thing is authentic! | ||
|
||
```sh | ||
$ pinki verify "$(cat /path/to/signing-key)" "$(cat /path/to/signature)" < /path/to/thing-that-was-signed | ||
OK | ||
``` | ||
|
||
The command will exit with status code 0 and print "OK" on success. | ||
Otherwise, it will exit with status code 1 and print an error message. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package main | ||
|
||
import ( | ||
"io" | ||
) | ||
|
||
func cli(args []string, in io.Reader, out io.Writer) error { | ||
if len(args) < 1 { | ||
return help(out) | ||
} | ||
|
||
switch args[0] { | ||
case "help": | ||
return help(out) | ||
case "key": | ||
if len(args) < 2 { | ||
return help(out) | ||
} | ||
|
||
switch args[1] { | ||
case "create": | ||
return createPrivateKey(out) | ||
case "export": | ||
return exportPublicKey(in, out) | ||
default: | ||
return help(out) | ||
} | ||
case "sign": | ||
if len(args) < 2 { | ||
return help(out) | ||
} | ||
|
||
return sign(args[1], in, out) | ||
case "verify": | ||
if len(args) < 3 { | ||
return help(out) | ||
} | ||
|
||
return verify(args[1], args[2], in, out) | ||
} | ||
|
||
return help(out) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
module github.com/twuni/pinki | ||
|
||
go 1.17 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package main | ||
|
||
import ( | ||
"errors" | ||
"io" | ||
) | ||
|
||
func help(out io.Writer) error { | ||
return errors.New(`USAGE: pinki <command> | ||
EXAMPLES | ||
$ pinki help | ||
Display this help message. | ||
$ pinki key create > private.pem | ||
Generate a new private key and write the result to "private.pem". | ||
$ pinki key export < private.pem > public.pem | ||
Extract the public key from "private.pem" and write the result to "public.pem". | ||
$ pinki sign "$(cat private.pem)" < package-1.0.0.tgz > package-1.0.0.tgz.asc | ||
Sign "package-1.0.0.tgz" using the private key from "private.pem" and write the result to "package-1.0.0.tgz.asc". | ||
$ pinki verify "$(cat public.pem)" "$(cat package-1.0.0.tgz.asc)" < package-1.0.0.tgz | ||
Verify the signature in "package-1.0.0.tgz.asc" of "package-1.0.0.tgz" using the public key from "public.pem". | ||
`) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package main | ||
|
||
import ( | ||
"crypto/ecdsa" | ||
"crypto/elliptic" | ||
"crypto/rand" | ||
"crypto/x509" | ||
"encoding/pem" | ||
"io" | ||
) | ||
|
||
func createPrivateKey(out io.Writer) error { | ||
key, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader) | ||
|
||
if err != nil { | ||
return err | ||
} | ||
|
||
der, err := x509.MarshalPKCS8PrivateKey(key) | ||
|
||
if err != nil { | ||
return err | ||
} | ||
|
||
block := &pem.Block{ | ||
Type: "PRIVATE KEY", | ||
Bytes: der, | ||
} | ||
|
||
return pem.Encode(out, block) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package main | ||
|
||
import ( | ||
"crypto/x509" | ||
"encoding/pem" | ||
"io" | ||
) | ||
|
||
func exportPublicKey(in io.Reader, out io.Writer) error { | ||
key, err := readPrivateKey(in) | ||
|
||
if err != nil { | ||
return err | ||
} | ||
|
||
der, err := x509.MarshalPKIXPublicKey(key.Public()) | ||
|
||
if err != nil { | ||
return err | ||
} | ||
|
||
block := &pem.Block{ | ||
Type: "PUBLIC KEY", | ||
Bytes: der, | ||
} | ||
|
||
return pem.Encode(out, block) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package main | ||
|
||
import ( | ||
"io" | ||
) | ||
|
||
func keyHelp(out io.Writer) error { | ||
out.Write([]byte("USAGE: pinki key create|export|help\n")) | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
) | ||
|
||
func main() { | ||
err := cli(os.Args[1:], os.Stdin, os.Stdout) | ||
|
||
if err != nil { | ||
fmt.Printf("%s\n", err) | ||
os.Exit(1) | ||
} | ||
|
||
os.Exit(0) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package main | ||
|
||
import ( | ||
"crypto/ecdsa" | ||
"crypto/x509" | ||
"encoding/pem" | ||
"errors" | ||
"io" | ||
) | ||
|
||
const ( | ||
PrivateKey = "PRIVATE KEY" | ||
) | ||
|
||
func readPrivateKey(in io.Reader) (*ecdsa.PrivateKey, error) { | ||
buffer, err := io.ReadAll(in) | ||
|
||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
block, _ := pem.Decode(buffer) | ||
|
||
if block == nil || block.Type != PrivateKey { | ||
return nil, errors.New("Expected a PEM-encoded private key") | ||
} | ||
|
||
key, err := x509.ParsePKCS8PrivateKey(block.Bytes) | ||
|
||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return key.(*ecdsa.PrivateKey), nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package main | ||
|
||
import ( | ||
"crypto/ecdsa" | ||
"crypto/x509" | ||
"encoding/pem" | ||
"errors" | ||
"io" | ||
) | ||
|
||
const ( | ||
PublicKey = "PUBLIC KEY" | ||
) | ||
|
||
func readPublicKey(in io.Reader) (*ecdsa.PublicKey, error) { | ||
buffer, err := io.ReadAll(in) | ||
|
||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
block, _ := pem.Decode(buffer) | ||
|
||
if block == nil || block.Type != PublicKey { | ||
return nil, errors.New("Expected a PEM-encoded public key") | ||
} | ||
|
||
key, err := x509.ParsePKIXPublicKey(block.Bytes) | ||
|
||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return key.(*ecdsa.PublicKey), nil | ||
} |
Oops, something went wrong.