diff --git a/.github/actions/build-gosop/action.yml b/.github/actions/build-gosop/action.yml index a754b67..64c20e1 100644 --- a/.github/actions/build-gosop/action.yml +++ b/.github/actions/build-gosop/action.yml @@ -16,7 +16,7 @@ runs: using: "composite" steps: - name: Checkout gopenpgp - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: ${{ inputs.gopenpgp-ref }} path: gopenpgp @@ -32,7 +32,7 @@ runs: with: go-version: ^1.18 - name: Check out gosop - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: ProtonMail/gosop ref: ${{ env.GOSOP_BRANCH_REF}} diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index e98a430..96ecc8d 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -30,7 +30,7 @@ jobs: link-to-sdk: true - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Build run: | @@ -41,7 +41,7 @@ jobs: find dist - name: Upload Android artifacts - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: Android build path: dist/android diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 303b8d2..059a2cf 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up latest golang uses: actions/setup-go@v3 @@ -27,7 +27,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up Go 1.17 uses: actions/setup-go@v3 @@ -44,7 +44,7 @@ jobs: - uses: actions/setup-go@v3 with: go-version: 1.17 - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: golangci-lint uses: golangci/golangci-lint-action@v3 with: diff --git a/.github/workflows/ios.yml b/.github/workflows/ios.yml index 9feeaa5..4e1f74d 100644 --- a/.github/workflows/ios.yml +++ b/.github/workflows/ios.yml @@ -15,7 +15,7 @@ jobs: - name: Set up xcode 14.3 uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: 14.3 + xcode-version: 15.3.0 id: xcode - name: Set up Go 1.x @@ -25,7 +25,7 @@ jobs: id: go - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Build env: @@ -35,7 +35,7 @@ jobs: find dist - name: Upload xcframework - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: gopenpgp.xcframework path: dist/apple/gopenpgp.xcframework diff --git a/.github/workflows/sop-test-suite.yml b/.github/workflows/sop-test-suite.yml index 77e987b..611eab8 100644 --- a/.github/workflows/sop-test-suite.yml +++ b/.github/workflows/sop-test-suite.yml @@ -11,14 +11,14 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Build gosop from branch uses: ./.github/actions/build-gosop with: binary-location: ./gosop-${{ github.sha }} # Upload as artifact - name: Upload gosop artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: gosop-${{ github.sha }} path: ./gosop-${{ github.sha }} @@ -28,7 +28,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Build gosop from branch uses: ./.github/actions/build-gosop with: @@ -36,7 +36,7 @@ jobs: binary-location: ./gosop-target # Upload as artifact - name: Upload gosop-target artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: gosop-target path: ./gosop-target @@ -55,10 +55,10 @@ jobs: - build-gosop-target steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 # Fetch gosop from target - name: Download gosop-target - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: gosop-target # Test gosop-target @@ -68,7 +68,7 @@ jobs: run: ./gosop-target version --extended # Fetch gosop from branch - name: Download gosop-branch - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: gosop-${{ github.sha }} - name: Rename gosop-branch @@ -94,12 +94,12 @@ jobs: RESULTS_HTML: .github/test-suite/test-suite-results.html # Upload results - name: Upload test results json artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: test-suite-results.json path: .github/test-suite/test-suite-results.json - name: Upload test results html artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: test-suite-results.html path: .github/test-suite/test-suite-results.html @@ -110,10 +110,10 @@ jobs: needs: test-suite steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Download test results json artifact id: download-test-results - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: test-suite-results.json - name: Compare with baseline diff --git a/CHANGELOG.md b/CHANGELOG.md index 5de4774..6d018ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,21 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [2.8.1] 2024-11-25 +### Changed +- Update go-crypto to `1.1.3`. + +## [2.8.0] 2024-10-07 +### Changed +- Update go-crypto to `1.1.0`. + +## [2.8.0-beta.0] 2024-10-01 +### Added +- Add `GetSHA256Fingerprint` method to `Key`. + +### Changed +- Update go-crypto to `1.1.0-beta.0`. + ## [2.8.0-alpha.1] 2024-04-09 ### Added diff --git a/constants/armor.go b/constants/armor.go index 8420af2..9a8b0c0 100644 --- a/constants/armor.go +++ b/constants/armor.go @@ -3,7 +3,7 @@ package constants // Constants for armored data. const ( - ArmorHeaderVersion = "GopenPGP 2.8.0" + ArmorHeaderVersion = "GopenPGP 2.8.1" ArmorHeaderComment = "https://gopenpgp.org" PGPMessageHeader = "PGP MESSAGE" PGPSignatureHeader = "PGP SIGNATURE" diff --git a/constants/version.go b/constants/version.go index 7ab3b2c..472adac 100644 --- a/constants/version.go +++ b/constants/version.go @@ -1,3 +1,3 @@ package constants -const Version = "2.8.0" +const Version = "2.8.1" diff --git a/crypto/key.go b/crypto/key.go index 16c50d9..7ab30b4 100644 --- a/crypto/key.go +++ b/crypto/key.go @@ -365,9 +365,14 @@ func (key *Key) GetFingerprint() string { return hex.EncodeToString(key.entity.PrimaryKey.Fingerprint) } +// GetSHA256Fingerprint computes the SHA256 fingerprint of the primary key. +func (key *Key) GetSHA256Fingerprint() (fingerprint string) { + return hex.EncodeToString(getSHA256FingerprintBytes(key.entity.PrimaryKey)) +} + // GetSHA256Fingerprints computes the SHA256 fingerprints of the key and subkeys. func (key *Key) GetSHA256Fingerprints() (fingerprints []string) { - fingerprints = append(fingerprints, hex.EncodeToString(getSHA256FingerprintBytes(key.entity.PrimaryKey))) + fingerprints = append(fingerprints, key.GetSHA256Fingerprint()) for _, sub := range key.entity.Subkeys { fingerprints = append(fingerprints, hex.EncodeToString(getSHA256FingerprintBytes(sub.PublicKey))) } diff --git a/crypto/key_test.go b/crypto/key_test.go index e80c6c0..e8c4771 100644 --- a/crypto/key_test.go +++ b/crypto/key_test.go @@ -374,6 +374,8 @@ func TestGetSHA256FingerprintsV4(t *testing.T) { assert.Len(t, sha256Fingerprints, 2) assert.Exactly(t, "d9ac0b857da6d2c8be985b251a9e3db31e7a1d2d832d1f07ebe838a9edce9c24", sha256Fingerprints[0]) assert.Exactly(t, "203dfba1f8442c17e59214d9cd11985bfc5cc8721bb4a71740dd5507e58a1a0d", sha256Fingerprints[1]) + + assert.Exactly(t, "d9ac0b857da6d2c8be985b251a9e3db31e7a1d2d832d1f07ebe838a9edce9c24", publicKey.GetSHA256Fingerprint()) } func TestGetEntity(t *testing.T) { diff --git a/crypto/signature.go b/crypto/signature.go index f5c58ab..b74ecc3 100644 --- a/crypto/signature.go +++ b/crypto/signature.go @@ -17,11 +17,15 @@ import ( "github.com/ProtonMail/gopenpgp/v2/internal" ) +// allowedHashes stores the allowed OpenPGP hashes that can be used in a +// message signature hash. var allowedHashes = []crypto.Hash{ crypto.SHA224, crypto.SHA256, crypto.SHA384, crypto.SHA512, + crypto.SHA3_256, + crypto.SHA3_512, } // SignatureVerificationError is returned from Decrypt and VerifyDetached diff --git a/crypto/signature_test.go b/crypto/signature_test.go index de32e1f..feca63d 100644 --- a/crypto/signature_test.go +++ b/crypto/signature_test.go @@ -301,7 +301,7 @@ func Test_SignDetachedWithNonCriticalContext(t *testing.T) { t.Fatal("Packet was not a signature") } notations := sig.Notations - if len(notations) != 1 { + if len(notations) != 2 { t.Fatal("Wrong number of notations") } notation := notations[0] @@ -344,7 +344,7 @@ func Test_SignDetachedWithCriticalContext(t *testing.T) { t.Fatal("Packet was not a signature") } notations := sig.Notations - if len(notations) != 1 { + if len(notations) != 2 { t.Fatal("Wrong number of notations") } notation := notations[0] diff --git a/go.mod b/go.mod index 8d21b6c..9dc0adb 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/ProtonMail/gopenpgp/v2 go 1.17 require ( - github.com/ProtonMail/go-crypto v1.1.0-alpha.1 + github.com/ProtonMail/go-crypto v1.1.3 github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.7.0 @@ -18,3 +18,5 @@ require ( golang.org/x/text v0.14.0 // indirect gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect ) + +replace github.com/ProtonMail/go-crypto => github.com/mssun/go-crypto v1.1.3-mssun-passforios diff --git a/go.sum b/go.sum index 260c580..0278620 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/ProtonMail/go-crypto v1.1.0-alpha.1 h1:iKLDnKGL+3u4Q5OjYgixAxWdkkGBPidCQumqVryUgtY= -github.com/ProtonMail/go-crypto v1.1.0-alpha.1/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= +github.com/ProtonMail/go-crypto v1.1.3 h1:nRBOetoydLeUb4nHajyO2bKqMLfWQ/ZPwkXqXxPxCFk= +github.com/ProtonMail/go-crypto v1.1.3/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f h1:tCbYj7/299ekTTXpdwKYF8eBlsYsDVoggDAuAjoK66k= github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f/go.mod h1:gcr0kNtGBqin9zDW9GOHcVntrwnjrK+qdJ06mWYBybw= github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= diff --git a/helper/helper.go b/helper/helper.go index 76c1f12..59d4024 100644 --- a/helper/helper.go +++ b/helper/helper.go @@ -4,8 +4,134 @@ package helper import ( "github.com/ProtonMail/gopenpgp/v2/crypto" "github.com/pkg/errors" + goerrors "errors" + pgpErrors "github.com/ProtonMail/go-crypto/openpgp/errors" + "github.com/ProtonMail/go-crypto/openpgp/packet" + "github.com/ProtonMail/go-crypto/openpgp" + "io" + "bytes" + "encoding/json" + "fmt" + "strconv" ) +func PassGetEncryptedMPI1(pgpMessage *crypto.PGPMessage) (encryptedMPI1 []byte, err error) { + var p packet.Packet + packets := packet.NewReader(pgpMessage.NewReader()) + var mpi1 []byte + +Loop1: + for { + if p, err = packets.Next(); goerrors.Is(err, io.EOF) { + break + } + + switch p := p.(type) { + case *packet.EncryptedKey: + mpi1 = p.GetEncryptedMPI1() + break Loop1 + } + } + + return mpi1, nil +} + +func PassGetEncryptedMPI2(pgpMessage *crypto.PGPMessage) (encryptedMPI2 []byte, err error) { + var p packet.Packet + packets := packet.NewReader(pgpMessage.NewReader()) + var mpi2 []byte + +Loop1: + for { + if p, err = packets.Next(); goerrors.Is(err, io.EOF) { + break + } + + switch p := p.(type) { + case *packet.EncryptedKey: + mpi2 = p.GetEncryptedMPI2() + break Loop1 + } + } + + return mpi2, nil +} + + +func PassGetHexSubkeyIDsJSON(key *crypto.Key) []byte { + subKeys := key.GetEntity().Subkeys + hexSubkeyIDs := make([]string, len(subKeys)) + for i, subKey := range subKeys { + hexSubkeyIDs[i] = fmt.Sprintf("%016v", strconv.FormatUint(subKey.PublicKey.KeyId, 16)) + } + + result, err := json.Marshal(hexSubkeyIDs) + if err != nil { + return nil + } + return result +} + + +func PassDecryptWithSessionKey(pgpMessage *crypto.PGPMessage, sk *crypto.SessionKey) (plain_message *crypto.PlainMessage, err error) { + var p packet.Packet + packets := packet.NewReader(pgpMessage.NewReader()) + var edp packet.EncryptedDataPacket + +Loop1: + for { + if p, err = packets.Next(); goerrors.Is(err, io.EOF) { + break + } + + switch p := p.(type) { + case *packet.SymmetricallyEncrypted, *packet.AEADEncrypted: + edp = p.(packet.EncryptedDataPacket) + break Loop1 + } + } + + var decrypted io.ReadCloser + f, err := sk.GetCipherFunc() + decrypted, err = edp.Decrypt(f, sk.Key) + packets.Push(decrypted) + + md := new(openpgp.MessageDetails) +Loop2: + for { + if p, err = packets.Next(); goerrors.Is(err, io.EOF) { + break + } + switch p := p.(type) { + case *packet.Compressed: + packets.Push(p.Body) + case *packet.LiteralData: + md.LiteralData = p + md.UnverifiedBody = md.LiteralData.Body + break Loop2 + } + } + + messageBuf := bytes.NewBuffer(nil) + _, err = io.Copy(messageBuf, md.UnverifiedBody) + if errors.Is(err, pgpErrors.ErrMDCHashMismatch) { + // This MDC error may also be triggered if the password is correct, but the encrypted data was corrupted. + // To avoid confusion, we do not inform the user about the second possibility. + return nil, errors.New("gopenpgp: wrong password in symmetric decryption") + } + if err != nil { + // Parsing errors after decryption, triggered before parsing the MDC packet, are also usually the result of wrong password + return nil, errors.New("gopenpgp: error in reading password protected message: wrong password or malformed message") + } + + return &crypto.PlainMessage{ + Data: messageBuf.Bytes(), + TextType: !md.LiteralData.IsBinary, + Filename: md.LiteralData.FileName, + Time: md.LiteralData.Time, + }, nil +} + // EncryptMessageWithPassword encrypts a string with a passphrase using AES256. func EncryptMessageWithPassword(password []byte, plaintext string) (ciphertext string, err error) { var pgpMessage *crypto.PGPMessage