commit b5f16b51cb5b89e653a66ee40a61acc6c39ebb6f Author: euphoria-laxis Date: Thu Jul 25 16:25:01 2024 +0200 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8962e3e --- /dev/null +++ b/.gitignore @@ -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/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..a633748 --- /dev/null +++ b/LICENSE @@ -0,0 +1,24 @@ +// euphoria-laxis.fr/go-packages/go-pgp@v0.1.0 +MIT License + +Copyright (c) 2020 Jason Chavannes + +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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..48cc646 --- /dev/null +++ b/README.md @@ -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.") +} +```` diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..26c6a76 --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module euphoria-laxis.fr/go-packages/go-pgp + +go 1.22 + +require golang.org/x/crypto v0.25.0 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..f7f8a2b --- /dev/null +++ b/go.sum @@ -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= diff --git a/pgp/decrypt.go b/pgp/decrypt.go new file mode 100644 index 0000000..116e96f --- /dev/null +++ b/pgp/decrypt.go @@ -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 +} diff --git a/pgp/encrypt_test.go b/pgp/encrypt_test.go new file mode 100644 index 0000000..49fc531 --- /dev/null +++ b/pgp/encrypt_test.go @@ -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-----` +) diff --git a/pgp/encypt.go b/pgp/encypt.go new file mode 100644 index 0000000..4700db5 --- /dev/null +++ b/pgp/encypt.go @@ -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 +} diff --git a/pgp/entity.go b/pgp/entity.go new file mode 100644 index 0000000..3defd83 --- /dev/null +++ b/pgp/entity.go @@ -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 +} diff --git a/pgp/generate.go b/pgp/generate.go new file mode 100644 index 0000000..91ac95b --- /dev/null +++ b/pgp/generate.go @@ -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 +} diff --git a/pgp/key.go b/pgp/key.go new file mode 100644 index 0000000..090a926 --- /dev/null +++ b/pgp/key.go @@ -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 +} diff --git a/pgp/sign.go b/pgp/sign.go new file mode 100644 index 0000000..50ebc0e --- /dev/null +++ b/pgp/sign.go @@ -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 +} diff --git a/pgp/sign_test.go b/pgp/sign_test.go new file mode 100644 index 0000000..54b1a5d --- /dev/null +++ b/pgp/sign_test.go @@ -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") +} diff --git a/pgp/verify.go b/pgp/verify.go new file mode 100644 index 0000000..c274b2b --- /dev/null +++ b/pgp/verify.go @@ -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 +}