Initial commit
This commit is contained in:
commit
b5f16b51cb
22
.gitignore
vendored
Normal file
22
.gitignore
vendored
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
### Go
|
||||||
|
# Binaries for programs and plugins
|
||||||
|
*.exe
|
||||||
|
*.exe~
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
|
||||||
|
# Test binary, built with `go test -c`
|
||||||
|
*.test
|
||||||
|
|
||||||
|
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||||
|
*.out
|
||||||
|
|
||||||
|
# Dependency directories (remove the comment below to include it)
|
||||||
|
vendor/
|
||||||
|
|
||||||
|
# Go workspace file
|
||||||
|
go.work
|
||||||
|
|
||||||
|
### GoLand
|
||||||
|
.idea/
|
24
LICENSE
Normal file
24
LICENSE
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
// euphoria-laxis.fr/go-packages/go-pgp@v0.1.0
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2020 Jason Chavannes <jason.chavannes@gmail.com>
|
||||||
|
|
||||||
|
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.
|
60
README.md
Normal file
60
README.md
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
# Go PGP
|
||||||
|
|
||||||
|
Layer on top of `golang.org/x/crypto/openpgp` to handle a few PGP use cases.
|
||||||
|
|
||||||
|
Forked from [jchavannes/go-pgp](https://github.com/jchavannes/go-pgp). Updated
|
||||||
|
to be used as a package.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Encryption
|
||||||
|
|
||||||
|
[pgp/encrypt_test.go](pgp/encrypt_test.go)
|
||||||
|
|
||||||
|
#### Encrypt
|
||||||
|
|
||||||
|
````go
|
||||||
|
// Create public key entity
|
||||||
|
publicKeyPacket, _ := pgp.GetPublicKeyPacket([]byte(TestPublicKey))
|
||||||
|
pubEntity, _ := pgp.CreateEntityFromKeys(publicKeyPacket, nil)
|
||||||
|
|
||||||
|
// Encrypt message
|
||||||
|
encrypted, _ := pgp.Encrypt(pubEntity, []byte(TestMessage))
|
||||||
|
````
|
||||||
|
|
||||||
|
#### Decrypt
|
||||||
|
|
||||||
|
````go
|
||||||
|
// Create private key entity
|
||||||
|
privEntity, _ := pgp.GetEntity([]byte(TestPublicKey), []byte(TestPrivateKey))
|
||||||
|
|
||||||
|
// Decrypt message
|
||||||
|
decrypted, _ := pgp.Decrypt(privEntity, encrypted)
|
||||||
|
````
|
||||||
|
|
||||||
|
### Signing
|
||||||
|
|
||||||
|
[pgp/sign_test.go](pgp/sign_test.go)
|
||||||
|
|
||||||
|
#### Sign
|
||||||
|
|
||||||
|
````go
|
||||||
|
// Create private key entity
|
||||||
|
entity, _ := pgp.GetEntity([]byte(TestPublicKey), []byte(TestPrivateKey))
|
||||||
|
|
||||||
|
// Sign message
|
||||||
|
signature, _ := pgp.Sign(entity, []byte(TestMessage))
|
||||||
|
````
|
||||||
|
|
||||||
|
#### Verify
|
||||||
|
|
||||||
|
````go
|
||||||
|
// Create public key packet
|
||||||
|
pubKeyPacket, _ := pgp.GetPublicKeyPacket([]byte(TestPublicKey))
|
||||||
|
|
||||||
|
// Verify signature
|
||||||
|
err = pgp.Verify(pubKeyPacket, []byte(TestMessage), signature)
|
||||||
|
if err == nil {
|
||||||
|
fmt.Println("Signature verified.")
|
||||||
|
}
|
||||||
|
````
|
5
go.mod
Normal file
5
go.mod
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
module euphoria-laxis.fr/go-packages/go-pgp
|
||||||
|
|
||||||
|
go 1.22
|
||||||
|
|
||||||
|
require golang.org/x/crypto v0.25.0
|
2
go.sum
Normal file
2
go.sum
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
|
||||||
|
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
|
39
pgp/decrypt.go
Normal file
39
pgp/decrypt.go
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
package pgp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
_ "crypto/sha256"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/openpgp"
|
||||||
|
"golang.org/x/crypto/openpgp/armor"
|
||||||
|
_ "golang.org/x/crypto/ripemd160"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Decrypt(entity *openpgp.Entity, encrypted []byte) ([]byte, error) {
|
||||||
|
// Decode message
|
||||||
|
block, err := armor.Decode(bytes.NewReader(encrypted))
|
||||||
|
if err != nil {
|
||||||
|
return []byte{}, fmt.Errorf("error decoding: %v", err)
|
||||||
|
}
|
||||||
|
if block.Type != "PGP MESSAGE" {
|
||||||
|
return []byte{}, errors.New("invalid message type")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrypt message
|
||||||
|
entityList := openpgp.EntityList{entity}
|
||||||
|
messageReader, err := openpgp.ReadMessage(block.Body, entityList, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
return []byte{}, fmt.Errorf("error reading message: %v", err)
|
||||||
|
}
|
||||||
|
var read []byte
|
||||||
|
read, err = ioutil.ReadAll(messageReader.UnverifiedBody)
|
||||||
|
if err != nil {
|
||||||
|
return []byte{}, fmt.Errorf("error reading unverified body: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return output - an unencrypted, and uncompressed message
|
||||||
|
return read, nil
|
||||||
|
}
|
106
pgp/encrypt_test.go
Normal file
106
pgp/encrypt_test.go
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
package pgp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/openpgp"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEncrypt(t *testing.T) {
|
||||||
|
fmt.Println("Encrypt test: START")
|
||||||
|
pubEntity, err := GetEntity([]byte(testPublicKey), []byte{})
|
||||||
|
if err != nil {
|
||||||
|
t.Error(fmt.Errorf("error getting entity: %v", err))
|
||||||
|
}
|
||||||
|
fmt.Println("Created public key entity.")
|
||||||
|
|
||||||
|
var encrypted []byte
|
||||||
|
encrypted, err = Encrypt(pubEntity, []byte(testMessage))
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
fmt.Println("Encrypted test message with public key entity.")
|
||||||
|
|
||||||
|
var privateEntity *openpgp.Entity
|
||||||
|
privateEntity, err = GetEntity([]byte(testPublicKey), []byte(testPrivateKey))
|
||||||
|
if err != nil {
|
||||||
|
t.Error(fmt.Errorf("error getting entity: %v", err))
|
||||||
|
}
|
||||||
|
fmt.Println("Created private key entity.")
|
||||||
|
|
||||||
|
var decrypted []byte
|
||||||
|
decrypted, err = Decrypt(privateEntity, encrypted)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
fmt.Println("Decrypted message with private key entity.")
|
||||||
|
|
||||||
|
decryptedMessage := string(decrypted)
|
||||||
|
if decryptedMessage != testMessage {
|
||||||
|
t.Error(fmt.Errorf("decrypted message does not equal original"))
|
||||||
|
}
|
||||||
|
fmt.Println("Decrypted message equals original message.")
|
||||||
|
fmt.Println("Encrypt test: END")
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
testMessage = "hello world"
|
||||||
|
testPrivateKey = `-----BEGIN PGP PRIVATE KEY BLOCK-----
|
||||||
|
|
||||||
|
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
|
||||||
|
=5NT7
|
||||||
|
-----END PGP PRIVATE KEY BLOCK-----`
|
||||||
|
testPublicKey = `-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||||
|
|
||||||
|
xsFNBFkNK+ABEADUpjJ/kz3j+iz9qnzUb6ONw+WHSLp8umnd1z06SBVkWFjYReqf
|
||||||
|
oPCOq67XDseK71ZSevrIt7EdTLAzl0xN8kB+8iedAGM5OCakDe3R8L83OGy1Em26
|
||||||
|
PbrrYs3TYKGDXW65TsGYCoETROGgU2zPvuBDU1RvVvd9vAlWHQis43BOWaaakCEc
|
||||||
|
00V3sdNcfV+lz7fNUXEgtmTCCr9NWX4gO3YeenIIxep4WD27VwscW5Q2B1cnxcFL
|
||||||
|
+TZzE2oVjtXljGSO94XsekuNU47zwJZNGyU6SSlSZ+KVXuSdkRRfNYHlgDWg5b8C
|
||||||
|
xVmdVUfsx3bmNlOlXoyETj83xvRlLxn3PYIgOz6OlYGba5oDogK2QLXGTXK1o9OE
|
||||||
|
kgoghmCNQqxocvb1hQXT8cEynIbAdc6/JknYaoic6ka1iTTz3uN8FEPw5gRlidcQ
|
||||||
|
3wkbmqIS0LJs3JmVbD7/BxMY1dwqMyvulfnLiTsWSPvk41o7dHf077t23V9w78Jg
|
||||||
|
h4Xq4HRvt37PtuO6eWW3c5aUIWmvvDqMbMEqp2y23noYoVNqEpVoHolDdoCSurv/
|
||||||
|
XxbNBnj46XwaIs6OlrO2htV0al2/WVTNnSLxCyoHXoJEDXyaOyNKn1jM/FczgYQJ
|
||||||
|
069uC804ohOfjLmbtUEYE7Hjeo5utPm2ryjnakgV5AStKgL0SyFZUwN/DwARAQAB
|
||||||
|
=gO1a
|
||||||
|
-----END PGP PUBLIC KEY BLOCK-----`
|
||||||
|
)
|
43
pgp/encypt.go
Normal file
43
pgp/encypt.go
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
package pgp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
_ "crypto/sha256"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/openpgp"
|
||||||
|
"golang.org/x/crypto/openpgp/armor"
|
||||||
|
_ "golang.org/x/crypto/ripemd160"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Encrypt(entity *openpgp.Entity, message []byte) ([]byte, error) {
|
||||||
|
// Create buffer to write output to
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
|
||||||
|
// Create encoder
|
||||||
|
encoderWriter, err := armor.Encode(buf, "PGP MESSAGE", make(map[string]string))
|
||||||
|
if err != nil {
|
||||||
|
return []byte{}, fmt.Errorf("error creating OpenPGP armor: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create encryptor with encoder
|
||||||
|
encryptorWriter, err := openpgp.Encrypt(encoderWriter, []*openpgp.Entity{entity}, nil, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
return []byte{}, fmt.Errorf("Error creating entity for encryption: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write message to compressor
|
||||||
|
messageReader := bytes.NewReader(message)
|
||||||
|
_, err = io.Copy(encryptorWriter, messageReader)
|
||||||
|
if err != nil {
|
||||||
|
return []byte{}, fmt.Errorf("Error writing data to message reader: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
//compressorWriter.Close()
|
||||||
|
encryptorWriter.Close()
|
||||||
|
encoderWriter.Close()
|
||||||
|
|
||||||
|
// Return buffer output - an encoded, encrypted, and compressed message
|
||||||
|
return buf.Bytes(), nil
|
||||||
|
}
|
84
pgp/entity.go
Normal file
84
pgp/entity.go
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
package pgp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/openpgp"
|
||||||
|
"golang.org/x/crypto/openpgp/packet"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetEntity(publicKey []byte, privateKey []byte) (*openpgp.Entity, error) {
|
||||||
|
publicKeyPacket, err := getPublicKeyPacket(publicKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var privateKeyPacket *packet.PrivateKey
|
||||||
|
if len(privateKey) > 0 {
|
||||||
|
privateKeyPacket, err = getPrivateKeyPacket(privateKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return createEntityFromKeys(publicKeyPacket, privateKeyPacket)
|
||||||
|
}
|
||||||
|
|
||||||
|
// From https://gist.github.com/eliquious/9e96017f47d9bd43cdf9
|
||||||
|
func createEntityFromKeys(pubKey *packet.PublicKey, privKey *packet.PrivateKey) (*openpgp.Entity, error) {
|
||||||
|
config := packet.Config{
|
||||||
|
DefaultHash: crypto.SHA256,
|
||||||
|
DefaultCipher: packet.CipherAES256,
|
||||||
|
DefaultCompressionAlgo: packet.CompressionZLIB,
|
||||||
|
CompressionConfig: &packet.CompressionConfig{
|
||||||
|
Level: 9,
|
||||||
|
},
|
||||||
|
RSABits: 4096,
|
||||||
|
}
|
||||||
|
currentTime := config.Now()
|
||||||
|
uid := packet.NewUserId("", "", "")
|
||||||
|
|
||||||
|
e := openpgp.Entity{
|
||||||
|
PrimaryKey: pubKey,
|
||||||
|
PrivateKey: privKey,
|
||||||
|
Identities: make(map[string]*openpgp.Identity),
|
||||||
|
}
|
||||||
|
isPrimaryId := false
|
||||||
|
|
||||||
|
e.Identities[uid.Id] = &openpgp.Identity{
|
||||||
|
Name: uid.Name,
|
||||||
|
UserId: uid,
|
||||||
|
SelfSignature: &packet.Signature{
|
||||||
|
CreationTime: currentTime,
|
||||||
|
SigType: packet.SigTypePositiveCert,
|
||||||
|
PubKeyAlgo: packet.PubKeyAlgoRSA,
|
||||||
|
Hash: config.Hash(),
|
||||||
|
IsPrimaryId: &isPrimaryId,
|
||||||
|
FlagsValid: true,
|
||||||
|
FlagSign: true,
|
||||||
|
FlagCertify: true,
|
||||||
|
IssuerKeyId: &e.PrimaryKey.KeyId,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
keyLifetimeSecs := uint32(86400 * 365)
|
||||||
|
|
||||||
|
e.Subkeys = make([]openpgp.Subkey, 1)
|
||||||
|
e.Subkeys[0] = openpgp.Subkey{
|
||||||
|
PublicKey: pubKey,
|
||||||
|
PrivateKey: privKey,
|
||||||
|
Sig: &packet.Signature{
|
||||||
|
CreationTime: currentTime,
|
||||||
|
SigType: packet.SigTypeSubkeyBinding,
|
||||||
|
PubKeyAlgo: packet.PubKeyAlgoRSA,
|
||||||
|
Hash: config.Hash(),
|
||||||
|
PreferredHash: []uint8{8}, // SHA-256
|
||||||
|
FlagsValid: true,
|
||||||
|
FlagEncryptStorage: true,
|
||||||
|
FlagEncryptCommunications: true,
|
||||||
|
IssuerKeyId: &e.PrimaryKey.KeyId,
|
||||||
|
KeyLifetimeSecs: &keyLifetimeSecs,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return &e, nil
|
||||||
|
}
|
51
pgp/generate.go
Normal file
51
pgp/generate.go
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
package pgp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/openpgp"
|
||||||
|
"golang.org/x/crypto/openpgp/armor"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PGPKeyPair struct {
|
||||||
|
PublicKey string
|
||||||
|
PrivateKey string
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenerateKeyPair(fullname string, comment string, email string) (PGPKeyPair, error) {
|
||||||
|
var e *openpgp.Entity
|
||||||
|
e, err := openpgp.NewEntity(fullname, comment, email, nil)
|
||||||
|
if err != nil {
|
||||||
|
return PGPKeyPair{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, id := range e.Identities {
|
||||||
|
err := id.SelfSignature.SignUserId(id.UserId.Id, e.PrimaryKey, e.PrivateKey, nil)
|
||||||
|
if err != nil {
|
||||||
|
return PGPKeyPair{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
w, err := armor.Encode(buf, openpgp.PublicKeyType, nil)
|
||||||
|
if err != nil {
|
||||||
|
return PGPKeyPair{}, err
|
||||||
|
}
|
||||||
|
e.Serialize(w)
|
||||||
|
w.Close()
|
||||||
|
pubKey := buf.String()
|
||||||
|
|
||||||
|
buf = new(bytes.Buffer)
|
||||||
|
w, err = armor.Encode(buf, openpgp.PrivateKeyType, nil)
|
||||||
|
if err != nil {
|
||||||
|
return PGPKeyPair{}, err
|
||||||
|
}
|
||||||
|
e.SerializePrivate(w, nil)
|
||||||
|
w.Close()
|
||||||
|
privateKey := buf.String()
|
||||||
|
|
||||||
|
return PGPKeyPair{
|
||||||
|
PublicKey: pubKey,
|
||||||
|
PrivateKey: privateKey,
|
||||||
|
}, nil
|
||||||
|
}
|
57
pgp/key.go
Normal file
57
pgp/key.go
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
package pgp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/openpgp"
|
||||||
|
"golang.org/x/crypto/openpgp/armor"
|
||||||
|
"golang.org/x/crypto/openpgp/packet"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getPublicKeyPacket(publicKey []byte) (*packet.PublicKey, error) {
|
||||||
|
publicKeyReader := bytes.NewReader(publicKey)
|
||||||
|
block, err := armor.Decode(publicKeyReader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if block.Type != openpgp.PublicKeyType {
|
||||||
|
return nil, errors.New("Invalid public key data")
|
||||||
|
}
|
||||||
|
|
||||||
|
packetReader := packet.NewReader(block.Body)
|
||||||
|
pkt, err := packetReader.Next()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
key, ok := pkt.(*packet.PublicKey)
|
||||||
|
if !ok {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return key, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPrivateKeyPacket(privateKey []byte) (*packet.PrivateKey, error) {
|
||||||
|
privateKeyReader := bytes.NewReader(privateKey)
|
||||||
|
block, err := armor.Decode(privateKeyReader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if block.Type != openpgp.PrivateKeyType {
|
||||||
|
return nil, errors.New("Invalid private key data")
|
||||||
|
}
|
||||||
|
|
||||||
|
packetReader := packet.NewReader(block.Body)
|
||||||
|
pkt, err := packetReader.Next()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
key, ok := pkt.(*packet.PrivateKey)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("Unable to cast to Private Key")
|
||||||
|
}
|
||||||
|
return key, nil
|
||||||
|
}
|
17
pgp/sign.go
Normal file
17
pgp/sign.go
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
package pgp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/openpgp"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Sign(entity *openpgp.Entity, message []byte) ([]byte, error) {
|
||||||
|
writer := new(bytes.Buffer)
|
||||||
|
reader := bytes.NewReader(message)
|
||||||
|
err := openpgp.ArmoredDetachSign(writer, entity, reader, nil)
|
||||||
|
if err != nil {
|
||||||
|
return []byte{}, err
|
||||||
|
}
|
||||||
|
return writer.Bytes(), nil
|
||||||
|
}
|
38
pgp/sign_test.go
Normal file
38
pgp/sign_test.go
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
package pgp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/openpgp"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSignature(t *testing.T) {
|
||||||
|
fmt.Println("Signature test: START")
|
||||||
|
entity, err := GetEntity([]byte(testPublicKey), []byte(testPrivateKey))
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
fmt.Println("Created private key entity.")
|
||||||
|
|
||||||
|
var signature []byte
|
||||||
|
signature, err = Sign(entity, []byte(testMessage))
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
fmt.Println("Created signature of test message with private key entity.")
|
||||||
|
|
||||||
|
var publicKeyEntity *openpgp.Entity
|
||||||
|
publicKeyEntity, err = GetEntity([]byte(testPublicKey), []byte{})
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
fmt.Println("Created public key entity.")
|
||||||
|
|
||||||
|
err = Verify(publicKeyEntity, []byte(testMessage), signature)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
fmt.Println("Signature verified using public key entity.")
|
||||||
|
fmt.Println("Signature test: END")
|
||||||
|
}
|
52
pgp/verify.go
Normal file
52
pgp/verify.go
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
package pgp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/openpgp"
|
||||||
|
"golang.org/x/crypto/openpgp/armor"
|
||||||
|
"golang.org/x/crypto/openpgp/packet"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Verify(publicKeyEntity *openpgp.Entity, message []byte, signature []byte) error {
|
||||||
|
sig, err := decodeSignature(signature)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
hash := sig.Hash.New()
|
||||||
|
messageReader := bytes.NewReader(message)
|
||||||
|
io.Copy(hash, messageReader)
|
||||||
|
|
||||||
|
err = publicKeyEntity.PrimaryKey.VerifySignature(hash, sig)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeSignature(signature []byte) (*packet.Signature, error) {
|
||||||
|
signatureReader := bytes.NewReader(signature)
|
||||||
|
block, err := armor.Decode(signatureReader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Error decoding OpenPGP Armor: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if block.Type != openpgp.SignatureType {
|
||||||
|
return nil, errors.New("Invalid signature file")
|
||||||
|
}
|
||||||
|
|
||||||
|
reader := packet.NewReader(block.Body)
|
||||||
|
pkt, err := reader.Next()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
sig, ok := pkt.(*packet.Signature)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("Error parsing signature")
|
||||||
|
}
|
||||||
|
return sig, nil
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user