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