├── openpgp ├── packet │ ├── config_v5.go │ ├── fuzz_test.go │ ├── recipient.go │ ├── packet_unsupported.go │ ├── notation.go │ ├── padding.go │ ├── marker.go │ ├── notation_test.go │ ├── aead_encrypted_data_test.go │ ├── ocfb_test.go │ ├── aead_config.go │ ├── compressed_test.go │ ├── literal.go │ ├── userid_test.go │ ├── opaque_test.go │ ├── symmetric_key_encrypted_data_test.go │ ├── userattribute.go │ ├── private_key_test_data.go │ ├── symmetrically_encrypted.go │ ├── aead_encrypted.go │ ├── public_key_test_data.go │ ├── ocfb.go │ ├── one_pass_signature.go │ ├── userid.go │ └── opaque.go ├── test_data │ ├── sym-corrupted-message-long-length.asc │ ├── sym-message-without-mdc.asc │ ├── argon2-sym-message.asc │ ├── sym-corrupted-message-invalid-sig-header.asc │ └── aead-ocb-asym-key.asc ├── hash.go ├── v2 │ ├── hash.go │ ├── canonical_text_test.go │ ├── keys_v5_test.go │ └── canonical_text.go ├── internal │ ├── encoding │ │ ├── encoding.go │ │ ├── oid_test.go │ │ ├── mpi_test.go │ │ ├── oid.go │ │ └── mpi.go │ ├── ecc │ │ ├── ed25519_test.go │ │ ├── curve25519_test.go │ │ ├── curves.go │ │ ├── x448.go │ │ ├── curve_info.go │ │ ├── ed25519.go │ │ ├── ed448.go │ │ └── generic.go │ └── algorithm │ │ ├── aead.go │ │ ├── cipher.go │ │ └── hash.go ├── s2k │ ├── s2k_cache.go │ └── s2k_config.go ├── canonical_text_test.go ├── ed448 │ ├── ed448_test.go │ └── ed448.go ├── x448 │ └── x448_test.go ├── x25519 │ └── x25519_test.go ├── ed25519 │ ├── ed25519_test.go │ └── ed25519.go ├── canonical_text.go ├── ecdsa │ ├── ecdsa.go │ └── ecdsa_test.go ├── elgamal │ ├── elgamal_test.go │ └── elgamal.go ├── eddsa │ ├── eddsa.go │ └── eddsa_test.go ├── keys_v5_test.go ├── ecdh │ └── ecdh_test.go ├── aes │ └── keywrap │ │ ├── keywrap_test.go │ │ └── keywrap.go ├── armor │ ├── armor.go │ └── encode.go └── keys_v6_test.go ├── .gitignore ├── AUTHORS ├── CONTRIBUTORS ├── go.mod ├── .github ├── test-suite │ ├── build_gosop_v1.sh │ ├── build_gosop.sh │ ├── prepare_config.sh │ └── config.json.template ├── CONTRIBUTING.md ├── workflows │ ├── go.yml │ └── codeql.yml └── actions │ └── build-gosop │ └── action.yml ├── .gitattributes ├── go.sum ├── README.md ├── brainpool ├── brainpool_test.go └── rcurve.go ├── ocb ├── rfc7253_test_vectors_suite_b.go └── rfc7253_test_vectors_suite_a.go ├── PATENTS ├── LICENSE ├── eax ├── eax_test_vectors.go └── eax.go └── internal └── byteutil └── byteutil.go /openpgp/packet/config_v5.go: -------------------------------------------------------------------------------- 1 | //go:build !v5 2 | 3 | package packet 4 | 5 | func init() { 6 | V5Disabled = true 7 | } 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Add no patterns to .gitignore except for files generated by the build. 2 | last-change 3 | .idea 4 | settings.json 5 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | # This source code refers to The Go Authors for copyright purposes. 2 | # The master list of authors is in the main Go distribution, 3 | # visible at https://tip.golang.org/AUTHORS. 4 | -------------------------------------------------------------------------------- /CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | # This source code was written by the Go contributors. 2 | # The master list of contributors is in the main Go distribution, 3 | # visible at https://tip.golang.org/CONTRIBUTORS. 4 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/ProtonMail/go-crypto 2 | 3 | go 1.22.0 4 | 5 | require ( 6 | github.com/cloudflare/circl v1.6.0 7 | golang.org/x/crypto v0.33.0 8 | ) 9 | 10 | require golang.org/x/sys v0.30.0 // indirect 11 | -------------------------------------------------------------------------------- /.github/test-suite/build_gosop_v1.sh: -------------------------------------------------------------------------------- 1 | cd gosop 2 | echo "replace github.com/ProtonMail/go-crypto => ../go-crypto" >> go.mod 3 | go get github.com/ProtonMail/go-crypto 4 | go get github.com/ProtonMail/gopenpgp/v2/crypto@v2.8.0-alpha.1 5 | go build . 6 | -------------------------------------------------------------------------------- /.github/test-suite/build_gosop.sh: -------------------------------------------------------------------------------- 1 | cd gosop 2 | echo "replace github.com/ProtonMail/go-crypto => ../go-crypto" >> go.mod 3 | go get github.com/ProtonMail/go-crypto 4 | go get github.com/ProtonMail/gopenpgp/v3/crypto@c09849a05e9a6221a99bc1dd8731f36d16ea1757 5 | go build . 6 | -------------------------------------------------------------------------------- /openpgp/test_data/sym-corrupted-message-long-length.asc: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP MESSAGE----- 2 | 3 | wy4ECQMIEShx0NrjpwoASRGkzSnkBylSXZgB4TSrf21Wq5HA8gb/KdXBlsSr 4 | PLUX0jwBxrt5T1ldEep/DOD8JAGW+z5z5eeZeOmlKNL6rx+GFl/ME2sv1Jxh 5 | IVub/p6L8KUvBPQEGT/UZj4A1hM= 6 | =S/F3 7 | -----END PGP MESSAGE----- -------------------------------------------------------------------------------- /openpgp/test_data/sym-message-without-mdc.asc: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP MESSAGE----- 2 | 3 | hF4DaLVM+JXUh9ESAQdA7E42KKhtMn7vjYN+CIcoM4QMXODako4DQPYLwhYjHTow 4 | hBVmQWk+WyyDjqB2yZEJPVTDYSvebBynwCGN9AFR6u5dXjFqtNNX6EJEJBzLLL2+ 5 | yUMNiMt8/ujn4y4GNaZEbFdf4+K/oQbz6fvQgNipWZGg2Ys0foHi50EzhTDyVG7s 6 | nwkVrIpSuhaZLoqzxbizw5o6YVMc 7 | =//7h 8 | -----END PGP MESSAGE----- 9 | -------------------------------------------------------------------------------- /openpgp/test_data/argon2-sym-message.asc: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP MESSAGE----- 2 | Comment: Encrypted using AES with 128-bit key 3 | Comment: Session key: 01FE16BBACFD1E7B78EF3B865187374F 4 | 5 | wycEBwScUvg8J/leUNU1RA7N/zE2AQQVnlL8rSLPP5VlQsunlO+ECxHSPgGYGKY+ 6 | YJz4u6F+DDlDBOr5NRQXt/KJIf4m4mOlKyC/uqLbpnLJZMnTq3o79GxBTdIdOzhH 7 | XfA3pqV4mTzF 8 | -----END PGP MESSAGE----- -------------------------------------------------------------------------------- /openpgp/packet/fuzz_test.go: -------------------------------------------------------------------------------- 1 | //go:build go1.18 2 | // +build go1.18 3 | 4 | package packet 5 | 6 | import ( 7 | "bytes" 8 | "testing" 9 | ) 10 | 11 | func FuzzPackets(f *testing.F) { 12 | f.Add([]byte("\x980\x040000\x16\t+\x06\x01\x04\x01\xdaG\x0f\x01\x00\x00")) 13 | f.Fuzz(func(t *testing.T, data []byte) { 14 | _, _ = Read(bytes.NewReader(data)) 15 | }) 16 | } 17 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Treat all files in this repo as binary, with no git magic updating 2 | # line endings. Windows users contributing to Go will need to use a 3 | # modern version of git and editors capable of LF line endings. 4 | # 5 | # We'll prevent accidental CRLF line endings from entering the repo 6 | # via the git-review gofmt checks. 7 | # 8 | # See golang.org/issue/9281 9 | 10 | * -text 11 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/cloudflare/circl v1.6.0 h1:cr5JKic4HI+LkINy2lg3W2jF8sHCVTBncJr5gIIq7qk= 2 | github.com/cloudflare/circl v1.6.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= 3 | golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= 4 | golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= 5 | golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= 6 | golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 7 | -------------------------------------------------------------------------------- /openpgp/packet/recipient.go: -------------------------------------------------------------------------------- 1 | package packet 2 | 3 | // Recipient type represents a Intended Recipient Fingerprint subpacket 4 | // See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh#name-intended-recipient-fingerpr 5 | type Recipient struct { 6 | KeyVersion int 7 | Fingerprint []byte 8 | } 9 | 10 | func (r *Recipient) Serialize() []byte { 11 | packet := make([]byte, len(r.Fingerprint)+1) 12 | packet[0] = byte(r.KeyVersion) 13 | copy(packet[1:], r.Fingerprint) 14 | return packet 15 | } 16 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribution Policy 2 | 3 | By making a contribution to this project: 4 | 5 | 1. I permanently license any and all copyright related to the contribution to Proton AG; 6 | 2. I certify that the contribution was created in whole by me; 7 | 3. I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it) is maintained indefinitely and may be redistributed with this project or the open source license(s) involved. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ``` 2 | go get github.com/ProtonMail/go-crypto 3 | ``` 4 | 5 | This module is backwards compatible with x/crypto/openpgp, 6 | so you can simply replace all imports of `golang.org/x/crypto/openpgp` with 7 | `github.com/ProtonMail/go-crypto/openpgp`. 8 | 9 | A partial list of changes is here: https://github.com/ProtonMail/go-crypto/issues/21#issuecomment-492792917. 10 | 11 | For the more extended API for reading and writing OpenPGP messages use `github.com/ProtonMail/go-crypto/openpgp/v2`, but it is not fully backwards compatible with `golang.org/x/crypto/openpgp`. 12 | -------------------------------------------------------------------------------- /.github/test-suite/prepare_config.sh: -------------------------------------------------------------------------------- 1 | CONFIG_TEMPLATE=$1 2 | CONFIG_OUTPUT=$2 3 | GOSOP_BRANCH_V1=$3 4 | GOSOP_BRANCH_V2=$4 5 | GOSOP_MAIN_V1=$5 6 | GOSOP_MAIN_V2=$6 7 | cat $CONFIG_TEMPLATE \ 8 | | sed "s@__GOSOP_BRANCH_V1__@${GOSOP_BRANCH_V1}@g" \ 9 | | sed "s@__GOSOP_BRANCH_V2__@${GOSOP_BRANCH_V2}@g" \ 10 | | sed "s@__GOSOP_MAIN_V1__@${GOSOP_MAIN_V1}@g" \ 11 | | sed "s@__GOSOP_MAIN_V2__@${GOSOP_MAIN_V2}@g" \ 12 | | sed "s@__SQOP__@${SQOP}@g" \ 13 | | sed "s@__GPGME_SOP__@${GPGME_SOP}@g" \ 14 | | sed "s@__SOP_OPENPGPJS__@${SOP_OPENPGPJS}@g" \ 15 | | sed "s@__RNP_SOP__@${RNP_SOP}@g" \ 16 | > $CONFIG_OUTPUT -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | 11 | test: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v3 15 | 16 | - name: Set up Go latest 17 | uses: actions/setup-go@v3 18 | with: 19 | go-version: ^1.22 20 | 21 | - name: Short test 22 | run: go test -short -v ./... 23 | 24 | - name: Randomized test suite 1 25 | run: go test -v ./... -run RandomizeFast -count=512 26 | 27 | - name: Randomized test suite 2 28 | run: go test -v ./... -run RandomizeSlow -count=32 29 | -------------------------------------------------------------------------------- /openpgp/packet/packet_unsupported.go: -------------------------------------------------------------------------------- 1 | package packet 2 | 3 | import ( 4 | "io" 5 | 6 | "github.com/ProtonMail/go-crypto/openpgp/errors" 7 | ) 8 | 9 | // UnsupportedPackage represents a OpenPGP packet with a known packet type 10 | // but with unsupported content. 11 | type UnsupportedPacket struct { 12 | IncompletePacket Packet 13 | Error errors.UnsupportedError 14 | } 15 | 16 | // Implements the Packet interface 17 | func (up *UnsupportedPacket) parse(read io.Reader) error { 18 | err := up.IncompletePacket.parse(read) 19 | if castedErr, ok := err.(errors.UnsupportedError); ok { 20 | up.Error = castedErr 21 | return nil 22 | } 23 | return err 24 | } 25 | -------------------------------------------------------------------------------- /openpgp/test_data/sym-corrupted-message-invalid-sig-header.asc: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP MESSAGE----- 2 | 3 | wy4ECQMI00eMTsbW8EsAFx4mj+GUgNLg0qVaJq/yy3n5pzkhy+Qvh9OP9Exr 4 | 93G00sCfAb0Oh0Nr/SpWimV1+8u+H1tbfBSN279p/bhbjJC8+7rdB2O7rXio 5 | Hn42u9wgdjuvPr5crFCboumKROMAYb5LIlvNxh50ERuXsdwpxxOOeR+8ShZa 6 | 7eAw/NcWcWKH3JnLVFi+TU+BgyyEnG3qikBSzoSpz/L+Hm65453pjodHyNYy 7 | s0WkZWPvNGk1pjPry4gi02YyZSuWlroc8mwiye8Scu+VSKQWqZZiY9XDXb1Z 8 | JEwfhYvaYd3dGxIPYKw8Lu61EUnX4vIGzmST4CWvuSkeUKy/W7Q1dVwT6+hZ 9 | Cai8gD5s6HLgOQqILidrMnc1I7txnlLvDp5R6R8QvyqCo+Qw1XyLZ7f0eLr7 10 | kI4BCTzGhPjcd+yFSd7QdSL6DjAyvNu4yIQcBee0aHnUxdK0oORxLnKpNT30 11 | C0JyDcXjrSiLF8MTafs0LRkzt5WJdqNiCEgwAnAzfFdm8jNEnHp9ZVstShWU 12 | wOk/CPfyDLf4mnFQn+q3h3NkFZXn/g== 13 | =odHC 14 | -----END PGP MESSAGE----- -------------------------------------------------------------------------------- /openpgp/hash.go: -------------------------------------------------------------------------------- 1 | package openpgp 2 | 3 | import ( 4 | "crypto" 5 | 6 | "github.com/ProtonMail/go-crypto/openpgp/internal/algorithm" 7 | ) 8 | 9 | // HashIdToHash returns a crypto.Hash which corresponds to the given OpenPGP 10 | // hash id. 11 | func HashIdToHash(id byte) (h crypto.Hash, ok bool) { 12 | return algorithm.HashIdToHash(id) 13 | } 14 | 15 | // HashIdToString returns the name of the hash function corresponding to the 16 | // given OpenPGP hash id. 17 | func HashIdToString(id byte) (name string, ok bool) { 18 | return algorithm.HashIdToString(id) 19 | } 20 | 21 | // HashToHashId returns an OpenPGP hash id which corresponds the given Hash. 22 | func HashToHashId(h crypto.Hash) (id byte, ok bool) { 23 | return algorithm.HashToHashId(h) 24 | } 25 | -------------------------------------------------------------------------------- /openpgp/v2/hash.go: -------------------------------------------------------------------------------- 1 | package v2 2 | 3 | import ( 4 | "crypto" 5 | 6 | "github.com/ProtonMail/go-crypto/openpgp/internal/algorithm" 7 | ) 8 | 9 | // HashIdToHash returns a crypto.Hash which corresponds to the given OpenPGP 10 | // hash id. 11 | func HashIdToHash(id byte) (h crypto.Hash, ok bool) { 12 | return algorithm.HashIdToHash(id) 13 | } 14 | 15 | // HashIdToString returns the name of the hash function corresponding to the 16 | // given OpenPGP hash id. 17 | func HashIdToString(id byte) (name string, ok bool) { 18 | return algorithm.HashIdToString(id) 19 | } 20 | 21 | // HashToHashId returns an OpenPGP hash id which corresponds the given Hash. 22 | func HashToHashId(h crypto.Hash) (id byte, ok bool) { 23 | return algorithm.HashToHashId(h) 24 | } 25 | -------------------------------------------------------------------------------- /openpgp/test_data/aead-ocb-asym-key.asc: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP PRIVATE KEY BLOCK----- 2 | Version: OpenPGP.js v4.6.2 3 | Comment: https://openpgpjs.org 4 | 5 | xVgEXZsWgRYJKwYBBAHaRw8BAQdAaOyGdv1sNIYJuOc14rVfRnhY3ZH/BOi/ 6 | Z+LF2as8t40AAP0b/Q6vTP9moJFRzgKNYxRvCRw0mFSEVfU2hoaHZVf9pg+D 7 | zQDCewQQFgoAIwUCXZsWgQYLCQcIAwIEFQgKAgMWAgECGQECGwMCHgMDIgEC 8 | AAoJEHsRWIsJTJyfxoQA/0sTO9zHTqfcwKmC56GHdNKW5hhfvXb9mvPZ1HRi 9 | dyjNAQCMdOZWKQCQpx1RpbLxd7yjP7aBTckfq9pyzP34SvoZCMddBF2bFoES 10 | CisGAQQBl1UBBQEBB0BOrEYuHCjpj+pymkh2teb4+CatfSBjHMGuN8//aNXe 11 | VgMBCAcAAP9858+87Racg9dX9J1f156x0vXaXpiEUA0UEgY0pjg4+BI5wmEE 12 | GBYIAAkFAl2bFoECGwwACgkQexFYiwlMnJ/8dgD/YE+lHy0/jtuYyZT6Ev+3 13 | xJ83bnlMlKJakNb2jY+TjAoA/2TFVwAzBv2coe/QGz/2fxNObsoQ6EKl797L 14 | m49PmVwF 15 | =5DeR 16 | -----END PGP PRIVATE KEY BLOCK----- 17 | -------------------------------------------------------------------------------- /.github/test-suite/config.json.template: -------------------------------------------------------------------------------- 1 | { 2 | "drivers": [ 3 | { 4 | "id": "gosop-branch-v1", 5 | "path": "__GOSOP_BRANCH_V1__" 6 | }, 7 | { 8 | "id": "gosop-branch-v2", 9 | "path": "__GOSOP_BRANCH_V2__" 10 | }, 11 | { 12 | "id": "gosop-main-v1", 13 | "path": "__GOSOP_MAIN_V1__" 14 | }, 15 | { 16 | "id": "gosop-main-v2", 17 | "path": "__GOSOP_MAIN_V2__" 18 | }, 19 | { 20 | "path": "__SQOP__" 21 | }, 22 | { 23 | "path": "__GPGME_SOP__" 24 | }, 25 | { 26 | "path": "__SOP_OPENPGPJS__" 27 | }, 28 | { 29 | "path": "__RNP_SOP__" 30 | } 31 | ], 32 | "rlimits": { 33 | "DATA": 1073741824 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /openpgp/packet/notation.go: -------------------------------------------------------------------------------- 1 | package packet 2 | 3 | // Notation type represents a Notation Data subpacket 4 | // see https://tools.ietf.org/html/rfc4880#section-5.2.3.16 5 | type Notation struct { 6 | Name string 7 | Value []byte 8 | IsCritical bool 9 | IsHumanReadable bool 10 | } 11 | 12 | func (notation *Notation) getData() []byte { 13 | nameData := []byte(notation.Name) 14 | nameLen := len(nameData) 15 | valueLen := len(notation.Value) 16 | 17 | data := make([]byte, 8+nameLen+valueLen) 18 | if notation.IsHumanReadable { 19 | data[0] = 0x80 20 | } 21 | 22 | data[4] = byte(nameLen >> 8) 23 | data[5] = byte(nameLen) 24 | data[6] = byte(valueLen >> 8) 25 | data[7] = byte(valueLen) 26 | copy(data[8:8+nameLen], nameData) 27 | copy(data[8+nameLen:], notation.Value) 28 | return data 29 | } 30 | -------------------------------------------------------------------------------- /openpgp/packet/padding.go: -------------------------------------------------------------------------------- 1 | package packet 2 | 3 | import ( 4 | "io" 5 | ) 6 | 7 | // Padding type represents a Padding Packet (Tag 21). 8 | // The padding type is represented by the length of its padding. 9 | // see https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh#name-padding-packet-tag-21 10 | type Padding int 11 | 12 | // parse just ignores the padding content. 13 | func (pad Padding) parse(reader io.Reader) error { 14 | _, err := io.CopyN(io.Discard, reader, int64(pad)) 15 | return err 16 | } 17 | 18 | // SerializePadding writes the padding to writer. 19 | func (pad Padding) SerializePadding(writer io.Writer, rand io.Reader) error { 20 | err := serializeHeader(writer, packetPadding, int(pad)) 21 | if err != nil { 22 | return err 23 | } 24 | _, err = io.CopyN(writer, rand, int64(pad)) 25 | return err 26 | } 27 | -------------------------------------------------------------------------------- /openpgp/packet/marker.go: -------------------------------------------------------------------------------- 1 | package packet 2 | 3 | import ( 4 | "io" 5 | 6 | "github.com/ProtonMail/go-crypto/openpgp/errors" 7 | ) 8 | 9 | type Marker struct{} 10 | 11 | const markerString = "PGP" 12 | 13 | // parse just checks if the packet contains "PGP". 14 | func (m *Marker) parse(reader io.Reader) error { 15 | var buffer [3]byte 16 | if _, err := io.ReadFull(reader, buffer[:]); err != nil { 17 | return err 18 | } 19 | if string(buffer[:]) != markerString { 20 | return errors.StructuralError("invalid marker packet") 21 | } 22 | return nil 23 | } 24 | 25 | // SerializeMarker writes a marker packet to writer. 26 | func SerializeMarker(writer io.Writer) error { 27 | err := serializeHeader(writer, packetTypeMarker, len(markerString)) 28 | if err != nil { 29 | return err 30 | } 31 | _, err = writer.Write([]byte(markerString)) 32 | return err 33 | } 34 | -------------------------------------------------------------------------------- /openpgp/internal/encoding/encoding.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package encoding implements openpgp packet field encodings as specified in 6 | // RFC 4880 and 6637. 7 | package encoding 8 | 9 | import "io" 10 | 11 | // Field is an encoded field of an openpgp packet. 12 | type Field interface { 13 | // Bytes returns the decoded data. 14 | Bytes() []byte 15 | 16 | // BitLength is the size in bits of the decoded data. 17 | BitLength() uint16 18 | 19 | // EncodedBytes returns the encoded data. 20 | EncodedBytes() []byte 21 | 22 | // EncodedLength is the size in bytes of the encoded data. 23 | EncodedLength() uint16 24 | 25 | // ReadFrom reads the next Field from r. 26 | ReadFrom(r io.Reader) (int64, error) 27 | } 28 | -------------------------------------------------------------------------------- /openpgp/internal/ecc/ed25519_test.go: -------------------------------------------------------------------------------- 1 | // Package ecc implements a generic interface for ECDH, ECDSA, and EdDSA. 2 | package ecc 3 | 4 | import ( 5 | "bytes" 6 | "crypto/rand" 7 | "io" 8 | "testing" 9 | ) 10 | 11 | // Test correct zero padding 12 | func TestEd25519MarshalUnmarshal(t *testing.T) { 13 | c := NewEd25519() 14 | 15 | x := make([]byte, ed25519Size) 16 | _, err := io.ReadFull(rand.Reader, x) 17 | if err != nil { 18 | t.Fatal(err) 19 | } 20 | 21 | x[0] = 0 22 | 23 | encoded := c.MarshalBytePoint(x) 24 | parsed := c.UnmarshalBytePoint(encoded) 25 | 26 | if !bytes.Equal(x, parsed) { 27 | t.Fatal("failed to marshal/unmarshal point correctly") 28 | } 29 | 30 | encoded = c.MarshalByteSecret(x) 31 | parsed = c.UnmarshalByteSecret(encoded) 32 | 33 | if !bytes.Equal(x, parsed) { 34 | t.Fatal("failed to marshal/unmarshal secret correctly") 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /openpgp/s2k/s2k_cache.go: -------------------------------------------------------------------------------- 1 | package s2k 2 | 3 | // Cache stores keys derived with s2k functions from one passphrase 4 | // to avoid recomputation if multiple items are encrypted with 5 | // the same parameters. 6 | type Cache map[Params][]byte 7 | 8 | // GetOrComputeDerivedKey tries to retrieve the key 9 | // for the given s2k parameters from the cache. 10 | // If there is no hit, it derives the key with the s2k function from the passphrase, 11 | // updates the cache, and returns the key. 12 | func (c *Cache) GetOrComputeDerivedKey(passphrase []byte, params *Params, expectedKeySize int) ([]byte, error) { 13 | key, found := (*c)[*params] 14 | if !found || len(key) != expectedKeySize { 15 | var err error 16 | derivedKey := make([]byte, expectedKeySize) 17 | s2k, err := params.Function() 18 | if err != nil { 19 | return nil, err 20 | } 21 | s2k(derivedKey, passphrase) 22 | (*c)[*params] = key 23 | return derivedKey, nil 24 | } 25 | return key, nil 26 | } 27 | -------------------------------------------------------------------------------- /brainpool/brainpool_test.go: -------------------------------------------------------------------------------- 1 | package brainpool 2 | 3 | import ( 4 | "crypto/ecdsa" 5 | "crypto/elliptic" 6 | "crypto/rand" 7 | "testing" 8 | ) 9 | 10 | func testCurve(t *testing.T, curve elliptic.Curve) { 11 | priv, err := ecdsa.GenerateKey(curve, rand.Reader) 12 | if err != nil { 13 | t.Fatal(err) 14 | } 15 | 16 | msg := []byte("test") 17 | r, s, err := ecdsa.Sign(rand.Reader, priv, msg) 18 | if err != nil { 19 | t.Fatal(err) 20 | } 21 | 22 | if !ecdsa.Verify(&priv.PublicKey, msg, r, s) { 23 | t.Fatal("signature didn't verify.") 24 | } 25 | } 26 | 27 | func TestP256t1(t *testing.T) { 28 | testCurve(t, P256t1()) 29 | } 30 | 31 | func TestP256r1(t *testing.T) { 32 | testCurve(t, P256r1()) 33 | } 34 | 35 | func TestP384t1(t *testing.T) { 36 | testCurve(t, P384t1()) 37 | } 38 | 39 | func TestP384r1(t *testing.T) { 40 | testCurve(t, P384r1()) 41 | } 42 | 43 | func TestP512t1(t *testing.T) { 44 | testCurve(t, P512t1()) 45 | } 46 | 47 | func TestP512r1(t *testing.T) { 48 | testCurve(t, P512r1()) 49 | } 50 | -------------------------------------------------------------------------------- /ocb/rfc7253_test_vectors_suite_b.go: -------------------------------------------------------------------------------- 1 | package ocb 2 | 3 | // Second set of test vectors from https://tools.ietf.org/html/rfc7253 4 | var rfc7253TestVectorTaglen96 = struct { 5 | key, nonce, header, plaintext, ciphertext string 6 | }{"0F0E0D0C0B0A09080706050403020100", 7 | "BBAA9988776655443322110D", 8 | "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627", 9 | "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627", 10 | "1792A4E31E0755FB03E31B22116E6C2DDF9EFD6E33D536F1A0124B0A55BAE884ED93481529C76B6AD0C515F4D1CDD4FDAC4F02AA"} 11 | 12 | var rfc7253AlgorithmTest = []struct { 13 | KEYLEN, TAGLEN int 14 | OUTPUT string 15 | }{ 16 | {128, 128, "67E944D23256C5E0B6C61FA22FDF1EA2"}, 17 | {192, 128, "F673F2C3E7174AAE7BAE986CA9F29E17"}, 18 | {256, 128, "D90EB8E9C977C88B79DD793D7FFA161C"}, 19 | {128, 96, "77A3D8E73589158D25D01209"}, 20 | {192, 96, "05D56EAD2752C86BE6932C5E"}, 21 | {256, 96, "5458359AC23B0CBA9E6330DD"}, 22 | {128, 64, "192C9B7BD90BA06A"}, 23 | {192, 64, "0066BC6E0EF34E24"}, 24 | {256, 64, "7D4EA5D445501CBE"}, 25 | } 26 | -------------------------------------------------------------------------------- /openpgp/packet/notation_test.go: -------------------------------------------------------------------------------- 1 | package packet 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | ) 7 | 8 | func TestNotationGetData(t *testing.T) { 9 | notation := Notation{ 10 | Name: "test@proton.me", 11 | Value: []byte("test-value"), 12 | IsCritical: true, 13 | IsHumanReadable: true, 14 | } 15 | expected := []byte{0x80, 0, 0, 0, 0, 14, 0, 10} 16 | expected = append(expected, []byte(notation.Name)...) 17 | expected = append(expected, []byte(notation.Value)...) 18 | data := notation.getData() 19 | if !bytes.Equal(expected, data) { 20 | t.Fatalf("Expected %s, got %s", expected, data) 21 | } 22 | } 23 | 24 | func TestNotationGetDataNotHumanReadable(t *testing.T) { 25 | notation := Notation{ 26 | Name: "test@proton.me", 27 | Value: []byte("test-value"), 28 | IsCritical: true, 29 | IsHumanReadable: false, 30 | } 31 | expected := []byte{0, 0, 0, 0, 0, 14, 0, 10} 32 | expected = append(expected, []byte(notation.Name)...) 33 | expected = append(expected, []byte(notation.Value)...) 34 | data := notation.getData() 35 | if !bytes.Equal(expected, data) { 36 | t.Fatalf("Expected %s, got %s", expected, data) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /openpgp/packet/aead_encrypted_data_test.go: -------------------------------------------------------------------------------- 1 | package packet 2 | 3 | // RFC4880bis, sec. A.3.4. 4 | 5 | // Includes both EAX and OCB samples 6 | var samplesAeadEncryptedDataPacket = []struct { 7 | mode, cek, adata, nonce, plaintext, ciphertext, tag, finalTag, full string 8 | chunkSize uint8 9 | }{ 10 | {"eax", 11 | "86f1efb86952329f24acd3bfd0e5346d", 12 | "d40107010e0000000000000000", 13 | "b732379f73c4928de25facfe6517ec10", 14 | "cb1462000000000048656c6c6f2c20776f726c64210a", 15 | "5dc11a81dc0cb8a2f6f3d90016384a56fc821ae11ae8", 16 | "dbcb49862655dea88d06a81486801b0f", 17 | "f387bd2eab013de1259586906eab2476", 18 | `d44a0107010eb732379f73c4928de25facfe6517ec105dc11a81dc0cb8a2f6f3d90016384a56fc821ae11ae8dbcb49862655dea88d06a81486801b0ff387bd2eab013de1259586906eab2476`, 19 | 14}, 20 | {"ocb", 21 | "d1f01ba30e130aa7d2582c16e050ae44", 22 | "d40107020e0000000000000000", 23 | "5ed2bc1e470abe8f1d644c7a6c8a56", 24 | "cb1462000000000048656c6c6f2c20776f726c64210a", 25 | "7b0f7701196611a154ba9c2574cd056284a8ef68035c", 26 | "623d93cc708a43211bb6eaf2b27f7c18", 27 | "d571bcd83b20add3a08b73af15b9a098", 28 | `d4490107020e5ed2bc1e470abe8f1d644c7a6c8a567b0f7701196611a154ba9c2574cd056284a8ef68035c623d93cc708a43211bb6eaf2b27f7c18d571bcd83b20add3a08b73af15b9a098`, 29 | 14}, 30 | } 31 | -------------------------------------------------------------------------------- /openpgp/internal/ecc/curve25519_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 ProtonTech AG. 2 | 3 | // Package ecc implements a generic interface for ECDH, ECDSA, and EdDSA. 4 | package ecc 5 | 6 | import ( 7 | "crypto/rand" 8 | "testing" 9 | ) 10 | 11 | // Some OpenPGP implementations, such as gpg 2.2.12, do not accept ECDH private 12 | // keys if they're not masked. This is because they're not of the proper form, 13 | // cryptographically, and they don't mask input keys during crypto operations. 14 | // This test checks if the keys that this library stores or outputs are 15 | // properly masked. 16 | func TestGenerateMaskedPrivateKeyX25519(t *testing.T) { 17 | c := NewCurve25519() 18 | _, secret, err := c.GenerateECDH(rand.Reader) 19 | if err != nil { 20 | t.Fatal(err) 21 | } 22 | 23 | encoded := c.MarshalByteSecret(secret) 24 | decoded := c.UnmarshalByteSecret(encoded) 25 | if decoded == nil { 26 | t.Fatal(err) 27 | } 28 | 29 | // Check masking 30 | // 3 lsb are 0 31 | if decoded[0]<<5 != 0 { 32 | t.Fatalf("Priv. key is not masked (3 lsb should be unset): %X", decoded) 33 | } 34 | // MSB is 0 35 | if decoded[31]>>7 != 0 { 36 | t.Fatalf("Priv. key is not masked (MSB should be unset): %X", decoded) 37 | } 38 | // Second-MSB is 1 39 | if decoded[31]>>6 != 1 { 40 | t.Fatalf("Priv. key is not masked (second MSB should be set): %X", decoded) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /PATENTS: -------------------------------------------------------------------------------- 1 | Additional IP Rights Grant (Patents) 2 | 3 | "This implementation" means the copyrightable works distributed by 4 | Google as part of the Go project. 5 | 6 | Google hereby grants to You a perpetual, worldwide, non-exclusive, 7 | no-charge, royalty-free, irrevocable (except as stated in this section) 8 | patent license to make, have made, use, offer to sell, sell, import, 9 | transfer and otherwise run, modify and propagate the contents of this 10 | implementation of Go, where such license applies only to those patent 11 | claims, both currently owned or controlled by Google and acquired in 12 | the future, licensable by Google that are necessarily infringed by this 13 | implementation of Go. This grant does not include claims that would be 14 | infringed only as a consequence of further modification of this 15 | implementation. If you or your agent or exclusive licensee institute or 16 | order or agree to the institution of patent litigation against any 17 | entity (including a cross-claim or counterclaim in a lawsuit) alleging 18 | that this implementation of Go or any code incorporated within this 19 | implementation of Go constitutes direct or contributory patent 20 | infringement, or inducement of patent infringement, then any patent 21 | rights granted to you under this License for this implementation of Go 22 | shall terminate as of the date such litigation is filed. 23 | -------------------------------------------------------------------------------- /openpgp/canonical_text_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package openpgp 6 | 7 | import ( 8 | "bytes" 9 | "testing" 10 | ) 11 | 12 | type recordingHash struct { 13 | buf *bytes.Buffer 14 | } 15 | 16 | func (r recordingHash) Write(b []byte) (n int, err error) { 17 | return r.buf.Write(b) 18 | } 19 | 20 | func (r recordingHash) Sum(in []byte) []byte { 21 | return append(in, r.buf.Bytes()...) 22 | } 23 | 24 | func (r recordingHash) Reset() { 25 | panic("shouldn't be called") 26 | } 27 | 28 | func (r recordingHash) Size() int { 29 | panic("shouldn't be called") 30 | } 31 | 32 | func (r recordingHash) BlockSize() int { 33 | panic("shouldn't be called") 34 | } 35 | 36 | func testCanonicalText(t *testing.T, input, expected string) { 37 | r := recordingHash{bytes.NewBuffer(nil)} 38 | c := NewCanonicalTextHash(r) 39 | c.Write([]byte(input)) 40 | result := c.Sum(nil) 41 | if expected != string(result) { 42 | t.Errorf("input: %x got: %x want: %x", input, result, expected) 43 | } 44 | } 45 | 46 | func TestCanonicalText(t *testing.T) { 47 | testCanonicalText(t, "foo\n", "foo\r\n") 48 | testCanonicalText(t, "foo", "foo") 49 | testCanonicalText(t, "foo\r\n", "foo\r\n") 50 | testCanonicalText(t, "foo\r\nbar", "foo\r\nbar") 51 | testCanonicalText(t, "foo\r\nbar\n\n", "foo\r\nbar\r\n\r\n") 52 | } 53 | -------------------------------------------------------------------------------- /openpgp/v2/canonical_text_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package v2 6 | 7 | import ( 8 | "bytes" 9 | "testing" 10 | ) 11 | 12 | type recordingHash struct { 13 | buf *bytes.Buffer 14 | } 15 | 16 | func (r recordingHash) Write(b []byte) (n int, err error) { 17 | return r.buf.Write(b) 18 | } 19 | 20 | func (r recordingHash) Sum(in []byte) []byte { 21 | return append(in, r.buf.Bytes()...) 22 | } 23 | 24 | func (r recordingHash) Reset() { 25 | panic("shouldn't be called") 26 | } 27 | 28 | func (r recordingHash) Size() int { 29 | panic("shouldn't be called") 30 | } 31 | 32 | func (r recordingHash) BlockSize() int { 33 | panic("shouldn't be called") 34 | } 35 | 36 | func testCanonicalText(t *testing.T, input, expected string) { 37 | r := recordingHash{bytes.NewBuffer(nil)} 38 | c := NewCanonicalTextHash(r) 39 | c.Write([]byte(input)) 40 | result := c.Sum(nil) 41 | if expected != string(result) { 42 | t.Errorf("input: %x got: %x want: %x", input, result, expected) 43 | } 44 | } 45 | 46 | func TestCanonicalText(t *testing.T) { 47 | testCanonicalText(t, "foo\n", "foo\r\n") 48 | testCanonicalText(t, "foo", "foo") 49 | testCanonicalText(t, "foo\r\n", "foo\r\n") 50 | testCanonicalText(t, "foo\r\nbar", "foo\r\nbar") 51 | testCanonicalText(t, "foo\r\nbar\n\n", "foo\r\nbar\r\n\r\n") 52 | } 53 | -------------------------------------------------------------------------------- /openpgp/ed448/ed448_test.go: -------------------------------------------------------------------------------- 1 | package ed448 2 | 3 | import ( 4 | "crypto/rand" 5 | "io" 6 | "testing" 7 | ) 8 | 9 | func TestGenerate(t *testing.T) { 10 | priv, err := GenerateKey(rand.Reader) 11 | if err != nil { 12 | t.Fatal(err) 13 | } 14 | if len(priv.Key) != SeedSize+PublicKeySize && len(priv.Point) != PublicKeySize { 15 | t.Error("gnerated wrong key sizes") 16 | } 17 | } 18 | 19 | func TestSignVerify(t *testing.T) { 20 | digest := make([]byte, 32) 21 | _, err := io.ReadFull(rand.Reader, digest[:]) 22 | if err != nil { 23 | t.Fatal(err) 24 | } 25 | 26 | priv, err := GenerateKey(rand.Reader) 27 | if err != nil { 28 | t.Fatal(err) 29 | } 30 | 31 | signature, err := Sign(priv, digest) 32 | if err != nil { 33 | t.Errorf("error signing: %s", err) 34 | } 35 | 36 | result := Verify(&priv.PublicKey, digest, signature) 37 | 38 | if !result { 39 | t.Error("unable to verify message") 40 | } 41 | 42 | digest[0] += 1 43 | result = Verify(&priv.PublicKey, digest, signature) 44 | 45 | if result { 46 | t.Error("signature should be invalid") 47 | } 48 | } 49 | 50 | func TestValidation(t *testing.T) { 51 | priv, err := GenerateKey(rand.Reader) 52 | if err != nil { 53 | t.Fatal(err) 54 | } 55 | if err := Validate(priv); err != nil { 56 | t.Fatalf("valid key marked as invalid: %s", err) 57 | } 58 | 59 | priv.Key[0] += 1 60 | if err := Validate(priv); err == nil { 61 | t.Fatal("failed to detect invalid key") 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /openpgp/packet/ocfb_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2010 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package packet 6 | 7 | import ( 8 | "bytes" 9 | "crypto/aes" 10 | "crypto/rand" 11 | "testing" 12 | ) 13 | 14 | var commonKey128 = []byte{0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c} 15 | 16 | func testOCFB(t *testing.T, resync OCFBResyncOption) { 17 | block, err := aes.NewCipher(commonKey128) 18 | if err != nil { 19 | t.Error(err) 20 | return 21 | } 22 | 23 | plaintext := []byte("this is the plaintext, which is long enough to span several blocks.") 24 | randData := make([]byte, block.BlockSize()) 25 | rand.Reader.Read(randData) 26 | ocfb, prefix := NewOCFBEncrypter(block, randData, resync) 27 | ciphertext := make([]byte, len(plaintext)) 28 | ocfb.XORKeyStream(ciphertext, plaintext) 29 | 30 | ocfbdec := NewOCFBDecrypter(block, prefix, resync) 31 | if ocfbdec == nil { 32 | t.Errorf("NewOCFBDecrypter failed (resync: %t)", resync) 33 | return 34 | } 35 | plaintextCopy := make([]byte, len(plaintext)) 36 | ocfbdec.XORKeyStream(plaintextCopy, ciphertext) 37 | 38 | if !bytes.Equal(plaintextCopy, plaintext) { 39 | t.Errorf("got: %x, want: %x (resync: %t)", plaintextCopy, plaintext, resync) 40 | } 41 | } 42 | 43 | func TestOCFB(t *testing.T) { 44 | testOCFB(t, OCFBNoResync) 45 | testOCFB(t, OCFBResync) 46 | } 47 | -------------------------------------------------------------------------------- /openpgp/x448/x448_test.go: -------------------------------------------------------------------------------- 1 | package x448 2 | 3 | import ( 4 | "bytes" 5 | "crypto/rand" 6 | "testing" 7 | ) 8 | 9 | func TestGenerate(t *testing.T) { 10 | privateKey, err := GenerateKey(rand.Reader) 11 | if err != nil { 12 | t.Fatal(err) 13 | } 14 | if len(privateKey.Secret) != KeySize { 15 | t.Fatal("key has the wrong size") 16 | } 17 | if len(privateKey.PublicKey.Point) != KeySize { 18 | t.Fatal("key has the wrong size") 19 | } 20 | } 21 | 22 | func TestValidate(t *testing.T) { 23 | privateKey, err := GenerateKey(rand.Reader) 24 | if err != nil { 25 | t.Fatal(err) 26 | } 27 | 28 | err = Validate(privateKey) 29 | if err != nil { 30 | t.Fatal(err) 31 | } 32 | privateKey.PublicKey.Point[0] = privateKey.PublicKey.Point[0] + byte(1) 33 | err = Validate(privateKey) 34 | if err == nil { 35 | t.Fatal("validation failed") 36 | } 37 | } 38 | 39 | func TestEncryptDecrypt(t *testing.T) { 40 | sessionKey := []byte("session.........") 41 | privateKey, err := GenerateKey(rand.Reader) 42 | if err != nil { 43 | t.Fatal(err) 44 | } 45 | 46 | ephemeralPublic, ctxt, err := Encrypt(rand.Reader, &privateKey.PublicKey, sessionKey) 47 | if err != nil { 48 | t.Errorf("error encrypting: %s", err) 49 | } 50 | 51 | sessionKeyAfter, err := Decrypt(privateKey, ephemeralPublic, ctxt) 52 | if err != nil { 53 | t.Errorf("error decrypting: %s", err) 54 | } 55 | 56 | if !bytes.Equal(sessionKeyAfter, sessionKey) { 57 | t.Errorf("decryption failed, got: %x, want: %x", sessionKeyAfter, sessionKey) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /openpgp/x25519/x25519_test.go: -------------------------------------------------------------------------------- 1 | package x25519 2 | 3 | import ( 4 | "bytes" 5 | "crypto/rand" 6 | "testing" 7 | ) 8 | 9 | func TestGenerate(t *testing.T) { 10 | privateKey, err := GenerateKey(rand.Reader) 11 | if err != nil { 12 | t.Fatal(err) 13 | } 14 | if len(privateKey.Secret) != KeySize { 15 | t.Fatal("key has the wrong size") 16 | } 17 | if len(privateKey.PublicKey.Point) != KeySize { 18 | t.Fatal("key has the wrong size") 19 | } 20 | } 21 | 22 | func TestValidate(t *testing.T) { 23 | privateKey, err := GenerateKey(rand.Reader) 24 | if err != nil { 25 | t.Fatal(err) 26 | } 27 | 28 | err = Validate(privateKey) 29 | if err != nil { 30 | t.Fatal(err) 31 | } 32 | privateKey.PublicKey.Point[0] = privateKey.PublicKey.Point[0] + byte(1) 33 | err = Validate(privateKey) 34 | if err == nil { 35 | t.Fatal("validation failed") 36 | } 37 | } 38 | 39 | func TestEncryptDecrypt(t *testing.T) { 40 | sessionKey := []byte("session.........") 41 | privateKey, err := GenerateKey(rand.Reader) 42 | if err != nil { 43 | t.Fatal(err) 44 | } 45 | 46 | ephemeralPublic, ctxt, err := Encrypt(rand.Reader, &privateKey.PublicKey, sessionKey) 47 | if err != nil { 48 | t.Errorf("error encrypting: %s", err) 49 | } 50 | 51 | sessionKeyAfter, err := Decrypt(privateKey, ephemeralPublic, ctxt) 52 | if err != nil { 53 | t.Errorf("error decrypting: %s", err) 54 | } 55 | 56 | if !bytes.Equal(sessionKeyAfter, sessionKey) { 57 | t.Errorf("decryption failed, got: %x, want: %x", sessionKeyAfter, sessionKey) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /openpgp/ed25519/ed25519_test.go: -------------------------------------------------------------------------------- 1 | package ed25519 2 | 3 | import ( 4 | "crypto/ed25519" 5 | "crypto/rand" 6 | "io" 7 | "testing" 8 | ) 9 | 10 | const messageDigestSize = 32 11 | 12 | func TestGenerate(t *testing.T) { 13 | priv, err := GenerateKey(rand.Reader) 14 | if err != nil { 15 | t.Fatal(err) 16 | } 17 | if len(priv.Key) != ed25519.SeedSize+ed25519.PublicKeySize && len(priv.Point) != ed25519.PublicKeySize { 18 | t.Error("generated wrong key sizes") 19 | } 20 | } 21 | 22 | func TestSignVerify(t *testing.T) { 23 | digest := make([]byte, messageDigestSize) 24 | _, err := io.ReadFull(rand.Reader, digest[:]) 25 | if err != nil { 26 | t.Fatal(err) 27 | } 28 | 29 | priv, err := GenerateKey(rand.Reader) 30 | if err != nil { 31 | t.Fatal(err) 32 | } 33 | 34 | signature, err := Sign(priv, digest) 35 | if err != nil { 36 | t.Errorf("error signing: %s", err) 37 | } 38 | 39 | result := Verify(&priv.PublicKey, digest, signature) 40 | 41 | if !result { 42 | t.Error("unable to verify message") 43 | } 44 | 45 | digest[0] += 1 46 | result = Verify(&priv.PublicKey, digest, signature) 47 | 48 | if result { 49 | t.Error("signature should be invalid") 50 | } 51 | } 52 | 53 | func TestValidation(t *testing.T) { 54 | priv, err := GenerateKey(rand.Reader) 55 | if err != nil { 56 | t.Fatal(err) 57 | } 58 | if err := Validate(priv); err != nil { 59 | t.Fatalf("valid key marked as invalid: %s", err) 60 | } 61 | 62 | priv.Key[0] += 1 63 | if err := Validate(priv); err == nil { 64 | t.Fatal("failed to detect invalid key") 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 The Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /openpgp/internal/algorithm/aead.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 ProtonTech AG 2 | 3 | package algorithm 4 | 5 | import ( 6 | "crypto/cipher" 7 | "github.com/ProtonMail/go-crypto/eax" 8 | "github.com/ProtonMail/go-crypto/ocb" 9 | ) 10 | 11 | // AEADMode defines the Authenticated Encryption with Associated Data mode of 12 | // operation. 13 | type AEADMode uint8 14 | 15 | // Supported modes of operation (see RFC4880bis [EAX] and RFC7253) 16 | const ( 17 | AEADModeEAX = AEADMode(1) 18 | AEADModeOCB = AEADMode(2) 19 | AEADModeGCM = AEADMode(3) 20 | ) 21 | 22 | // TagLength returns the length in bytes of authentication tags. 23 | func (mode AEADMode) TagLength() int { 24 | switch mode { 25 | case AEADModeEAX: 26 | return 16 27 | case AEADModeOCB: 28 | return 16 29 | case AEADModeGCM: 30 | return 16 31 | default: 32 | return 0 33 | } 34 | } 35 | 36 | // NonceLength returns the length in bytes of nonces. 37 | func (mode AEADMode) NonceLength() int { 38 | switch mode { 39 | case AEADModeEAX: 40 | return 16 41 | case AEADModeOCB: 42 | return 15 43 | case AEADModeGCM: 44 | return 12 45 | default: 46 | return 0 47 | } 48 | } 49 | 50 | // New returns a fresh instance of the given mode 51 | func (mode AEADMode) New(block cipher.Block) (alg cipher.AEAD) { 52 | var err error 53 | switch mode { 54 | case AEADModeEAX: 55 | alg, err = eax.NewEAX(block) 56 | case AEADModeOCB: 57 | alg, err = ocb.NewOCB(block) 58 | case AEADModeGCM: 59 | alg, err = cipher.NewGCM(block) 60 | } 61 | if err != nil { 62 | panic(err.Error()) 63 | } 64 | return alg 65 | } 66 | -------------------------------------------------------------------------------- /openpgp/canonical_text.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package openpgp 6 | 7 | import ( 8 | "hash" 9 | "io" 10 | ) 11 | 12 | // NewCanonicalTextHash reformats text written to it into the canonical 13 | // form and then applies the hash h. See RFC 4880, section 5.2.1. 14 | func NewCanonicalTextHash(h hash.Hash) hash.Hash { 15 | return &canonicalTextHash{h, 0} 16 | } 17 | 18 | type canonicalTextHash struct { 19 | h hash.Hash 20 | s int 21 | } 22 | 23 | var newline = []byte{'\r', '\n'} 24 | 25 | func writeCanonical(cw io.Writer, buf []byte, s *int) (int, error) { 26 | start := 0 27 | for i, c := range buf { 28 | switch *s { 29 | case 0: 30 | if c == '\r' { 31 | *s = 1 32 | } else if c == '\n' { 33 | if _, err := cw.Write(buf[start:i]); err != nil { 34 | return 0, err 35 | } 36 | if _, err := cw.Write(newline); err != nil { 37 | return 0, err 38 | } 39 | start = i + 1 40 | } 41 | case 1: 42 | *s = 0 43 | } 44 | } 45 | 46 | if _, err := cw.Write(buf[start:]); err != nil { 47 | return 0, err 48 | } 49 | return len(buf), nil 50 | } 51 | 52 | func (cth *canonicalTextHash) Write(buf []byte) (int, error) { 53 | return writeCanonical(cth.h, buf, &cth.s) 54 | } 55 | 56 | func (cth *canonicalTextHash) Sum(in []byte) []byte { 57 | return cth.h.Sum(in) 58 | } 59 | 60 | func (cth *canonicalTextHash) Reset() { 61 | cth.h.Reset() 62 | cth.s = 0 63 | } 64 | 65 | func (cth *canonicalTextHash) Size() int { 66 | return cth.h.Size() 67 | } 68 | 69 | func (cth *canonicalTextHash) BlockSize() int { 70 | return cth.h.BlockSize() 71 | } 72 | -------------------------------------------------------------------------------- /openpgp/internal/ecc/curves.go: -------------------------------------------------------------------------------- 1 | // Package ecc implements a generic interface for ECDH, ECDSA, and EdDSA. 2 | package ecc 3 | 4 | import ( 5 | "io" 6 | "math/big" 7 | ) 8 | 9 | type Curve interface { 10 | GetCurveName() string 11 | } 12 | 13 | type ECDSACurve interface { 14 | Curve 15 | MarshalIntegerPoint(x, y *big.Int) []byte 16 | UnmarshalIntegerPoint([]byte) (x, y *big.Int) 17 | MarshalIntegerSecret(d *big.Int) []byte 18 | UnmarshalIntegerSecret(d []byte) *big.Int 19 | GenerateECDSA(rand io.Reader) (x, y, secret *big.Int, err error) 20 | Sign(rand io.Reader, x, y, d *big.Int, hash []byte) (r, s *big.Int, err error) 21 | Verify(x, y *big.Int, hash []byte, r, s *big.Int) bool 22 | ValidateECDSA(x, y *big.Int, secret []byte) error 23 | } 24 | 25 | type EdDSACurve interface { 26 | Curve 27 | MarshalBytePoint(x []byte) []byte 28 | UnmarshalBytePoint([]byte) (x []byte) 29 | MarshalByteSecret(d []byte) []byte 30 | UnmarshalByteSecret(d []byte) []byte 31 | MarshalSignature(sig []byte) (r, s []byte) 32 | UnmarshalSignature(r, s []byte) (sig []byte) 33 | GenerateEdDSA(rand io.Reader) (pub, priv []byte, err error) 34 | Sign(publicKey, privateKey, message []byte) (sig []byte, err error) 35 | Verify(publicKey, message, sig []byte) bool 36 | ValidateEdDSA(publicKey, privateKey []byte) (err error) 37 | } 38 | type ECDHCurve interface { 39 | Curve 40 | MarshalBytePoint([]byte) (encoded []byte) 41 | UnmarshalBytePoint(encoded []byte) []byte 42 | MarshalByteSecret(d []byte) []byte 43 | UnmarshalByteSecret(d []byte) []byte 44 | GenerateECDH(rand io.Reader) (point []byte, secret []byte, err error) 45 | Encaps(rand io.Reader, point []byte) (ephemeral, sharedSecret []byte, err error) 46 | Decaps(ephemeral, secret []byte) (sharedSecret []byte, err error) 47 | ValidateECDH(public []byte, secret []byte) error 48 | } 49 | -------------------------------------------------------------------------------- /.github/actions/build-gosop/action.yml: -------------------------------------------------------------------------------- 1 | name: 'build-gosop' 2 | description: 'Build gosop from the current branch' 3 | 4 | inputs: 5 | 6 | go-crypto-ref: 7 | description: 'go-crypto branch tag or commit to build from' 8 | required: true 9 | default: '' 10 | 11 | binary-location: 12 | description: 'Path for the gosop binary' 13 | required: true 14 | default: './gosop-${{ github.sha }}' 15 | 16 | branch-gosop: 17 | description: 'Branch of the gosop to use' 18 | required: false 19 | default: 'main' 20 | 21 | gosop-build-path: 22 | description: 'Build script of the gosop to use' 23 | required: false 24 | default: 'build_gosop_v1.sh' 25 | 26 | runs: 27 | using: "composite" 28 | steps: 29 | - name: Checkout go-crypto 30 | uses: actions/checkout@v3 31 | with: 32 | ref: ${{ inputs.go-crypto-ref }} 33 | path: go-crypto 34 | # Build gosop 35 | - name: Set up latest golang 36 | uses: actions/setup-go@v3 37 | with: 38 | go-version: ^1.18 39 | - name: Check out gosop 40 | uses: actions/checkout@v3 41 | with: 42 | repository: ProtonMail/gosop 43 | ref: ${{ inputs.branch-gosop }} 44 | path: gosop 45 | - name: Cache go modules 46 | uses: actions/cache@v3 47 | with: 48 | path: | 49 | ~/.cache/go-build 50 | ~/go/pkg/mod 51 | key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} 52 | restore-keys: | 53 | ${{ runner.os }}-go- 54 | - name: Build gosop 55 | run: ./.github/test-suite/${{ inputs.gosop-build-path }} 56 | shell: bash 57 | # Test the binary 58 | - name: Print gosop version 59 | run: ./gosop/gosop version --extended 60 | shell: bash 61 | # Move and rename binary 62 | - name: Move binary 63 | run: mv gosop/gosop ${{ inputs.binary-location }} 64 | shell: bash 65 | 66 | -------------------------------------------------------------------------------- /openpgp/packet/aead_config.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 ProtonTech AG 2 | 3 | package packet 4 | 5 | import "math/bits" 6 | 7 | // CipherSuite contains a combination of Cipher and Mode 8 | type CipherSuite struct { 9 | // The cipher function 10 | Cipher CipherFunction 11 | // The AEAD mode of operation. 12 | Mode AEADMode 13 | } 14 | 15 | // AEADConfig collects a number of AEAD parameters along with sensible defaults. 16 | // A nil AEADConfig is valid and results in all default values. 17 | type AEADConfig struct { 18 | // The AEAD mode of operation. 19 | DefaultMode AEADMode 20 | // Amount of octets in each chunk of data 21 | ChunkSize uint64 22 | } 23 | 24 | // Mode returns the AEAD mode of operation. 25 | func (conf *AEADConfig) Mode() AEADMode { 26 | // If no preference is specified, OCB is used (which is mandatory to implement). 27 | if conf == nil || conf.DefaultMode == 0 { 28 | return AEADModeOCB 29 | } 30 | 31 | mode := conf.DefaultMode 32 | if mode != AEADModeEAX && mode != AEADModeOCB && mode != AEADModeGCM { 33 | panic("AEAD mode unsupported") 34 | } 35 | return mode 36 | } 37 | 38 | // ChunkSizeByte returns the byte indicating the chunk size. The effective 39 | // chunk size is computed with the formula uint64(1) << (chunkSizeByte + 6) 40 | // limit chunkSizeByte to 16 which equals to 2^22 = 4 MiB 41 | // https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-07.html#section-5.13.2 42 | func (conf *AEADConfig) ChunkSizeByte() byte { 43 | if conf == nil || conf.ChunkSize == 0 { 44 | return 12 // 1 << (12 + 6) == 262144 bytes 45 | } 46 | 47 | chunkSize := conf.ChunkSize 48 | exponent := bits.Len64(chunkSize) - 1 49 | switch { 50 | case exponent < 6: 51 | exponent = 6 52 | case exponent > 22: 53 | exponent = 22 54 | } 55 | 56 | return byte(exponent - 6) 57 | } 58 | 59 | // decodeAEADChunkSize returns the effective chunk size. In 32-bit systems, the 60 | // maximum returned value is 1 << 30. 61 | func decodeAEADChunkSize(c byte) int { 62 | size := uint64(1 << (c + 6)) 63 | if size != uint64(int(size)) { 64 | return 1 << 30 65 | } 66 | return int(size) 67 | } 68 | -------------------------------------------------------------------------------- /openpgp/ecdsa/ecdsa.go: -------------------------------------------------------------------------------- 1 | // Package ecdsa implements ECDSA signature, suitable for OpenPGP, 2 | // as specified in RFC 6637, section 5. 3 | package ecdsa 4 | 5 | import ( 6 | "errors" 7 | "github.com/ProtonMail/go-crypto/openpgp/internal/ecc" 8 | "io" 9 | "math/big" 10 | ) 11 | 12 | type PublicKey struct { 13 | X, Y *big.Int 14 | curve ecc.ECDSACurve 15 | } 16 | 17 | type PrivateKey struct { 18 | PublicKey 19 | D *big.Int 20 | } 21 | 22 | func NewPublicKey(curve ecc.ECDSACurve) *PublicKey { 23 | return &PublicKey{ 24 | curve: curve, 25 | } 26 | } 27 | 28 | func NewPrivateKey(key PublicKey) *PrivateKey { 29 | return &PrivateKey{ 30 | PublicKey: key, 31 | } 32 | } 33 | 34 | func (pk *PublicKey) GetCurve() ecc.ECDSACurve { 35 | return pk.curve 36 | } 37 | 38 | func (pk *PublicKey) MarshalPoint() []byte { 39 | return pk.curve.MarshalIntegerPoint(pk.X, pk.Y) 40 | } 41 | 42 | func (pk *PublicKey) UnmarshalPoint(p []byte) error { 43 | pk.X, pk.Y = pk.curve.UnmarshalIntegerPoint(p) 44 | if pk.X == nil { 45 | return errors.New("ecdsa: failed to parse EC point") 46 | } 47 | return nil 48 | } 49 | 50 | func (sk *PrivateKey) MarshalIntegerSecret() []byte { 51 | return sk.curve.MarshalIntegerSecret(sk.D) 52 | } 53 | 54 | func (sk *PrivateKey) UnmarshalIntegerSecret(d []byte) error { 55 | sk.D = sk.curve.UnmarshalIntegerSecret(d) 56 | 57 | if sk.D == nil { 58 | return errors.New("ecdsa: failed to parse scalar") 59 | } 60 | return nil 61 | } 62 | 63 | func GenerateKey(rand io.Reader, c ecc.ECDSACurve) (priv *PrivateKey, err error) { 64 | priv = new(PrivateKey) 65 | priv.PublicKey.curve = c 66 | priv.PublicKey.X, priv.PublicKey.Y, priv.D, err = c.GenerateECDSA(rand) 67 | return 68 | } 69 | 70 | func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err error) { 71 | return priv.PublicKey.curve.Sign(rand, priv.X, priv.Y, priv.D, hash) 72 | } 73 | 74 | func Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool { 75 | return pub.curve.Verify(pub.X, pub.Y, hash, r, s) 76 | } 77 | 78 | func Validate(priv *PrivateKey) error { 79 | return priv.curve.ValidateECDSA(priv.X, priv.Y, priv.D.Bytes()) 80 | } 81 | -------------------------------------------------------------------------------- /openpgp/elgamal/elgamal_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package elgamal 6 | 7 | import ( 8 | "bytes" 9 | "crypto/rand" 10 | "math/big" 11 | "testing" 12 | ) 13 | 14 | // This is the 1024-bit MODP group from RFC 5114, section 2.1: 15 | const primeHex = "B10B8F96A080E01DDE92DE5EAE5D54EC52C99FBCFB06A3C69A6A9DCA52D23B616073E28675A23D189838EF1E2EE652C013ECB4AEA906112324975C3CD49B83BFACCBDD7D90C4BD7098488E9C219A73724EFFD6FAE5644738FAA31A4FF55BCCC0A151AF5F0DC8B4BD45BF37DF365C1A65E68CFDA76D4DA708DF1FB2BC2E4A4371" 16 | 17 | const generatorHex = "A4D1CBD5C3FD34126765A442EFB99905F8104DD258AC507FD6406CFF14266D31266FEA1E5C41564B777E690F5504F213160217B4B01B886A5E91547F9E2749F4D7FBD7D3B9A92EE1909D0D2263F80A76A6A24C087A091F531DBF0A0169B6A28AD662A4D18E73AFA32D779D5918D08BC8858F4DCEF97C2A24855E6EEB22B3B2E5" 18 | 19 | func fromHex(hex string) *big.Int { 20 | n, ok := new(big.Int).SetString(hex, 16) 21 | if !ok { 22 | panic("failed to parse hex number") 23 | } 24 | return n 25 | } 26 | 27 | func TestEncryptDecrypt(t *testing.T) { 28 | priv := &PrivateKey{ 29 | PublicKey: PublicKey{ 30 | G: fromHex(generatorHex), 31 | P: fromHex(primeHex), 32 | }, 33 | X: fromHex("42"), 34 | } 35 | priv.Y = new(big.Int).Exp(priv.G, priv.X, priv.P) 36 | 37 | message := []byte("hello world") 38 | c1, c2, err := Encrypt(rand.Reader, &priv.PublicKey, message) 39 | if err != nil { 40 | t.Errorf("error encrypting: %s", err) 41 | } 42 | message2, err := Decrypt(priv, c1, c2) 43 | if err != nil { 44 | t.Errorf("error decrypting: %s", err) 45 | } 46 | if !bytes.Equal(message2, message) { 47 | t.Errorf("decryption failed, got: %x, want: %x", message2, message) 48 | } 49 | } 50 | 51 | func TestDecryptBadKey(t *testing.T) { 52 | priv := &PrivateKey{ 53 | PublicKey: PublicKey{ 54 | G: fromHex(generatorHex), 55 | P: fromHex("2"), 56 | }, 57 | X: fromHex("42"), 58 | } 59 | priv.Y = new(big.Int).Exp(priv.G, priv.X, priv.P) 60 | c1, c2 := fromHex("8"), fromHex("8") 61 | if _, err := Decrypt(priv, c1, c2); err == nil { 62 | t.Errorf("unexpected success decrypting") 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /openpgp/packet/compressed_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package packet 6 | 7 | import ( 8 | "bytes" 9 | "crypto/rand" 10 | "encoding/hex" 11 | "io" 12 | mathrand "math/rand" 13 | "testing" 14 | ) 15 | 16 | const ( 17 | maxMessageLen = 1 << 12 18 | ) 19 | 20 | func TestCompressed(t *testing.T) { 21 | packet, err := Read(readerFromHex(compressedHex)) 22 | if err != nil { 23 | t.Errorf("failed to read Compressed: %s", err) 24 | return 25 | } 26 | 27 | c, ok := packet.(*Compressed) 28 | if !ok { 29 | t.Error("didn't find Compressed packet") 30 | return 31 | } 32 | 33 | contents, err := io.ReadAll(c.Body) 34 | if err != nil && err != io.EOF { 35 | t.Error(err) 36 | return 37 | } 38 | 39 | expected, _ := hex.DecodeString(compressedExpectedHex) 40 | if !bytes.Equal(expected, contents) { 41 | t.Errorf("got:%x want:%x", contents, expected) 42 | } 43 | } 44 | 45 | const compressedHex = "a3013b2d90c4e02b72e25f727e5e496a5e49b11e1700" 46 | const compressedExpectedHex = "cb1062004d14c8fe636f6e74656e74732e0a" 47 | 48 | func TestCompressDecompressRandomizeFast(t *testing.T) { 49 | algorithms := []CompressionAlgo{ 50 | CompressionZIP, 51 | CompressionZLIB, 52 | } 53 | plaintext := make([]byte, mathrand.Intn(maxMessageLen)) 54 | rand.Read(plaintext) 55 | algo := algorithms[mathrand.Intn(len(algorithms))] 56 | compConfig := &CompressionConfig{ 57 | Level: -1 + mathrand.Intn(11), 58 | } 59 | w := bytes.NewBuffer(nil) 60 | wc := &noOpCloser{w: w} 61 | wcomp, err := SerializeCompressed(wc, algo, compConfig) 62 | if err != nil { 63 | t.Fatal(err) 64 | } 65 | // Compress to w 66 | wcomp.Write(plaintext) 67 | wcomp.Close() 68 | // Read the packet and decompress 69 | p, err := Read(w) 70 | if err != nil { 71 | t.Fatal(err) 72 | } 73 | c, ok := p.(*Compressed) 74 | if !ok { 75 | t.Error("didn't find Compressed packet") 76 | } 77 | contents, err := io.ReadAll(c.Body) 78 | if err != nil && err != io.EOF { 79 | t.Error(err) 80 | } 81 | if !bytes.Equal(contents, plaintext) { 82 | t.Error("Could not retrieve original after decompress") 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /eax/eax_test_vectors.go: -------------------------------------------------------------------------------- 1 | package eax 2 | 3 | // Test vectors from 4 | // https://web.cs.ucdavis.edu/~rogaway/papers/eax.pdf 5 | var testVectors = []struct { 6 | msg, key, nonce, header, ciphertext string 7 | }{ 8 | {"", 9 | "233952DEE4D5ED5F9B9C6D6FF80FF478", 10 | "62EC67F9C3A4A407FCB2A8C49031A8B3", 11 | "6BFB914FD07EAE6B", 12 | "E037830E8389F27B025A2D6527E79D01"}, 13 | {"F7FB", 14 | "91945D3F4DCBEE0BF45EF52255F095A4", 15 | "BECAF043B0A23D843194BA972C66DEBD", 16 | "FA3BFD4806EB53FA", 17 | "19DD5C4C9331049D0BDAB0277408F67967E5"}, 18 | {"1A47CB4933", 19 | "01F74AD64077F2E704C0F60ADA3DD523", 20 | "70C3DB4F0D26368400A10ED05D2BFF5E", 21 | "234A3463C1264AC6", 22 | "D851D5BAE03A59F238A23E39199DC9266626C40F80"}, 23 | {"481C9E39B1", 24 | "D07CF6CBB7F313BDDE66B727AFD3C5E8", 25 | "8408DFFF3C1A2B1292DC199E46B7D617", 26 | "33CCE2EABFF5A79D", 27 | "632A9D131AD4C168A4225D8E1FF755939974A7BEDE"}, 28 | {"40D0C07DA5E4", 29 | "35B6D0580005BBC12B0587124557D2C2", 30 | "FDB6B06676EEDC5C61D74276E1F8E816", 31 | "AEB96EAEBE2970E9", 32 | "071DFE16C675CB0677E536F73AFE6A14B74EE49844DD"}, 33 | {"4DE3B35C3FC039245BD1FB7D", 34 | "BD8E6E11475E60B268784C38C62FEB22", 35 | "6EAC5C93072D8E8513F750935E46DA1B", 36 | "D4482D1CA78DCE0F", 37 | "835BB4F15D743E350E728414ABB8644FD6CCB86947C5E10590210A4F"}, 38 | {"8B0A79306C9CE7ED99DAE4F87F8DD61636", 39 | "7C77D6E813BED5AC98BAA417477A2E7D", 40 | "1A8C98DCD73D38393B2BF1569DEEFC19", 41 | "65D2017990D62528", 42 | "02083E3979DA014812F59F11D52630DA30137327D10649B0AA6E1C181DB617D7F2"}, 43 | {"1BDA122BCE8A8DBAF1877D962B8592DD2D56", 44 | "5FFF20CAFAB119CA2FC73549E20F5B0D", 45 | "DDE59B97D722156D4D9AFF2BC7559826", 46 | "54B9F04E6A09189A", 47 | "2EC47B2C4954A489AFC7BA4897EDCDAE8CC33B60450599BD02C96382902AEF7F832A"}, 48 | {"6CF36720872B8513F6EAB1A8A44438D5EF11", 49 | "A4A4782BCFFD3EC5E7EF6D8C34A56123", 50 | "B781FCF2F75FA5A8DE97A9CA48E522EC", 51 | "899A175897561D7E", 52 | "0DE18FD0FDD91E7AF19F1D8EE8733938B1E8E7F6D2231618102FDB7FE55FF1991700"}, 53 | {"CA40D7446E545FFAED3BD12A740A659FFBBB3CEAB7", 54 | "8395FCF1E95BEBD697BD010BC766AAC3", 55 | "22E7ADD93CFC6393C57EC0B3C17D6B44", 56 | "126735FCC320D25A", 57 | "CB8920F87A6C75CFF39627B56E3ED197C552D295A7CFC46AFC253B4652B1AF3795B124AB6E"}, 58 | } 59 | -------------------------------------------------------------------------------- /openpgp/eddsa/eddsa.go: -------------------------------------------------------------------------------- 1 | // Package eddsa implements EdDSA signature, suitable for OpenPGP, as specified in 2 | // https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-13.7 3 | package eddsa 4 | 5 | import ( 6 | "errors" 7 | "github.com/ProtonMail/go-crypto/openpgp/internal/ecc" 8 | "io" 9 | ) 10 | 11 | type PublicKey struct { 12 | X []byte 13 | curve ecc.EdDSACurve 14 | } 15 | 16 | type PrivateKey struct { 17 | PublicKey 18 | D []byte 19 | } 20 | 21 | func NewPublicKey(curve ecc.EdDSACurve) *PublicKey { 22 | return &PublicKey{ 23 | curve: curve, 24 | } 25 | } 26 | 27 | func NewPrivateKey(key PublicKey) *PrivateKey { 28 | return &PrivateKey{ 29 | PublicKey: key, 30 | } 31 | } 32 | 33 | func (pk *PublicKey) GetCurve() ecc.EdDSACurve { 34 | return pk.curve 35 | } 36 | 37 | func (pk *PublicKey) MarshalPoint() []byte { 38 | return pk.curve.MarshalBytePoint(pk.X) 39 | } 40 | 41 | func (pk *PublicKey) UnmarshalPoint(x []byte) error { 42 | pk.X = pk.curve.UnmarshalBytePoint(x) 43 | 44 | if pk.X == nil { 45 | return errors.New("eddsa: failed to parse EC point") 46 | } 47 | return nil 48 | } 49 | 50 | func (sk *PrivateKey) MarshalByteSecret() []byte { 51 | return sk.curve.MarshalByteSecret(sk.D) 52 | } 53 | 54 | func (sk *PrivateKey) UnmarshalByteSecret(d []byte) error { 55 | sk.D = sk.curve.UnmarshalByteSecret(d) 56 | 57 | if sk.D == nil { 58 | return errors.New("eddsa: failed to parse scalar") 59 | } 60 | return nil 61 | } 62 | 63 | func GenerateKey(rand io.Reader, c ecc.EdDSACurve) (priv *PrivateKey, err error) { 64 | priv = new(PrivateKey) 65 | priv.PublicKey.curve = c 66 | priv.PublicKey.X, priv.D, err = c.GenerateEdDSA(rand) 67 | return 68 | } 69 | 70 | func Sign(priv *PrivateKey, message []byte) (r, s []byte, err error) { 71 | sig, err := priv.PublicKey.curve.Sign(priv.PublicKey.X, priv.D, message) 72 | if err != nil { 73 | return nil, nil, err 74 | } 75 | 76 | r, s = priv.PublicKey.curve.MarshalSignature(sig) 77 | return 78 | } 79 | 80 | func Verify(pub *PublicKey, message, r, s []byte) bool { 81 | sig := pub.curve.UnmarshalSignature(r, s) 82 | if sig == nil { 83 | return false 84 | } 85 | 86 | return pub.curve.Verify(pub.X, message, sig) 87 | } 88 | 89 | func Validate(priv *PrivateKey) error { 90 | return priv.curve.ValidateEdDSA(priv.PublicKey.X, priv.D) 91 | } 92 | -------------------------------------------------------------------------------- /openpgp/internal/encoding/oid_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package encoding 6 | 7 | import ( 8 | "bytes" 9 | "io" 10 | "testing" 11 | 12 | "github.com/ProtonMail/go-crypto/openpgp/errors" 13 | ) 14 | 15 | var oidTests = []struct { 16 | encoded []byte 17 | bytes []byte 18 | bitLength uint16 19 | err error 20 | }{ 21 | { 22 | encoded: []byte{0x1, 0x1}, 23 | bytes: []byte{0x1}, 24 | bitLength: 8, 25 | }, 26 | { 27 | encoded: []byte{0x2, 0x1, 0x2}, 28 | bytes: []byte{0x1, 0x2}, 29 | bitLength: 16, 30 | }, 31 | { 32 | encoded: []byte{0xa, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa}, 33 | bytes: []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa}, 34 | bitLength: 80, 35 | }, 36 | // extension overlap errors 37 | { 38 | encoded: []byte{0x0}, 39 | err: errors.UnsupportedError("reserved for future extensions"), 40 | }, 41 | { 42 | encoded: append([]byte{0xff}, make([]byte, 0xff)...), 43 | err: errors.UnsupportedError("reserved for future extensions"), 44 | }, 45 | // EOF error, 46 | { 47 | encoded: []byte{}, 48 | err: io.ErrUnexpectedEOF, 49 | }, 50 | { 51 | encoded: []byte{0xa}, 52 | err: io.ErrUnexpectedEOF, 53 | }, 54 | } 55 | 56 | func TestOID(t *testing.T) { 57 | for i, test := range oidTests { 58 | oid := new(OID) 59 | if _, err := oid.ReadFrom(bytes.NewBuffer(test.encoded)); err != nil { 60 | if !sameError(err, test.err) { 61 | t.Errorf("#%d: ReadFrom error got:%q", i, err) 62 | } 63 | continue 64 | } 65 | if b := oid.Bytes(); !bytes.Equal(b, test.bytes) { 66 | t.Errorf("#%d: bad creation got:%x want:%x", i, b, test.bytes) 67 | } 68 | if bl := oid.BitLength(); bl != test.bitLength { 69 | t.Errorf("#%d: bad BitLength got:%d want:%d", i, bl, test.bitLength) 70 | } 71 | if b := oid.EncodedBytes(); !bytes.Equal(b, test.encoded) { 72 | t.Errorf("#%d: bad encoding got:%x want:%x", i, b, test.encoded) 73 | } 74 | } 75 | } 76 | 77 | func sameError(err1, err2 error) bool { 78 | switch { 79 | case err1 == err2: 80 | return true 81 | case err1 == nil, err2 == nil: 82 | return false 83 | default: 84 | return err1.Error() == err2.Error() 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /openpgp/internal/encoding/mpi_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package encoding 6 | 7 | import ( 8 | "bytes" 9 | "io" 10 | "testing" 11 | ) 12 | 13 | var mpiTests = []struct { 14 | encoded []byte 15 | bytes []byte 16 | reencoded []byte 17 | bitLength uint16 18 | err error 19 | }{ 20 | { 21 | encoded: []byte{0x0, 0x0}, 22 | bytes: []byte{}, 23 | bitLength: 0, 24 | }, 25 | { 26 | encoded: []byte{0x0, 0x1, 0x1}, 27 | bytes: []byte{0x1}, 28 | bitLength: 1, 29 | }, 30 | { 31 | encoded: []byte{0x0, 0x9, 0x1, 0xff}, 32 | bytes: []byte{0x1, 0xff}, 33 | bitLength: 9, 34 | }, 35 | { 36 | encoded: append([]byte{0x1, 0x0, 0xff}, make([]byte, 0x1f)...), 37 | bytes: append([]byte{0xff}, make([]byte, 0x1f)...), 38 | bitLength: 0x100, 39 | }, 40 | // https://bugs.gnupg.org/gnupg/issue1853 41 | { 42 | encoded: []byte{0x0, 0x10, 0x0, 0x1}, 43 | bytes: []byte{0x0, 0x1}, 44 | reencoded: []byte{0x0, 0x1, 0x1}, 45 | bitLength: 0x10, 46 | }, 47 | // EOF error, 48 | { 49 | encoded: []byte{}, 50 | err: io.ErrUnexpectedEOF, 51 | }, 52 | { 53 | encoded: []byte{0x1, 0x0, 0x0}, 54 | err: io.ErrUnexpectedEOF, 55 | }, 56 | } 57 | 58 | func TestMPI(t *testing.T) { 59 | for i, test := range mpiTests { 60 | mpi := new(MPI) 61 | if _, err := mpi.ReadFrom(bytes.NewBuffer(test.encoded)); err != nil { 62 | if !sameError(err, test.err) { 63 | t.Errorf("#%d: ReadFrom error got:%q", i, err) 64 | } 65 | continue 66 | } 67 | if b := mpi.Bytes(); !bytes.Equal(b, test.bytes) { 68 | t.Errorf("#%d: bad creation got:%x want:%x", i, b, test.bytes) 69 | } 70 | if bl := mpi.BitLength(); bl != test.bitLength { 71 | t.Errorf("#%d: bad BitLength got:%d want:%d", i, bl, test.bitLength) 72 | } 73 | 74 | reencoded := test.encoded 75 | if test.reencoded != nil { 76 | reencoded = test.reencoded 77 | } 78 | 79 | if b := mpi.EncodedBytes(); !bytes.Equal(b, test.encoded) { 80 | t.Errorf("#%d: bad encoding got:%x want:%x", i, b, test.encoded) 81 | } 82 | if b := NewMPI(mpi.Bytes()).EncodedBytes(); !bytes.Equal(b, reencoded) { 83 | t.Errorf("#%d: bad encoding got:%x want:%x", i, b, reencoded) 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /openpgp/packet/literal.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package packet 6 | 7 | import ( 8 | "encoding/binary" 9 | "io" 10 | ) 11 | 12 | // LiteralData represents an encrypted file. See RFC 4880, section 5.9. 13 | type LiteralData struct { 14 | Format uint8 15 | IsBinary bool 16 | FileName string 17 | Time uint32 // Unix epoch time. Either creation time or modification time. 0 means undefined. 18 | Body io.Reader 19 | } 20 | 21 | // ForEyesOnly returns whether the contents of the LiteralData have been marked 22 | // as especially sensitive. 23 | func (l *LiteralData) ForEyesOnly() bool { 24 | return l.FileName == "_CONSOLE" 25 | } 26 | 27 | func (l *LiteralData) parse(r io.Reader) (err error) { 28 | var buf [256]byte 29 | 30 | _, err = readFull(r, buf[:2]) 31 | if err != nil { 32 | return 33 | } 34 | 35 | l.Format = buf[0] 36 | l.IsBinary = l.Format == 'b' 37 | fileNameLen := int(buf[1]) 38 | 39 | _, err = readFull(r, buf[:fileNameLen]) 40 | if err != nil { 41 | return 42 | } 43 | 44 | l.FileName = string(buf[:fileNameLen]) 45 | 46 | _, err = readFull(r, buf[:4]) 47 | if err != nil { 48 | return 49 | } 50 | 51 | l.Time = binary.BigEndian.Uint32(buf[:4]) 52 | l.Body = r 53 | return 54 | } 55 | 56 | // SerializeLiteral serializes a literal data packet to w and returns a 57 | // WriteCloser to which the data itself can be written and which MUST be closed 58 | // on completion. The fileName is truncated to 255 bytes. 59 | func SerializeLiteral(w io.WriteCloser, isBinary bool, fileName string, time uint32) (plaintext io.WriteCloser, err error) { 60 | var buf [4]byte 61 | buf[0] = 'b' 62 | if !isBinary { 63 | buf[0] = 'u' 64 | } 65 | if len(fileName) > 255 { 66 | fileName = fileName[:255] 67 | } 68 | buf[1] = byte(len(fileName)) 69 | 70 | inner, err := serializeStreamHeader(w, packetTypeLiteralData) 71 | if err != nil { 72 | return 73 | } 74 | 75 | _, err = inner.Write(buf[:2]) 76 | if err != nil { 77 | return 78 | } 79 | _, err = inner.Write([]byte(fileName)) 80 | if err != nil { 81 | return 82 | } 83 | binary.BigEndian.PutUint32(buf[:], time) 84 | _, err = inner.Write(buf[:]) 85 | if err != nil { 86 | return 87 | } 88 | 89 | plaintext = inner 90 | return 91 | } 92 | -------------------------------------------------------------------------------- /openpgp/internal/encoding/oid.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package encoding 6 | 7 | import ( 8 | "io" 9 | 10 | "github.com/ProtonMail/go-crypto/openpgp/errors" 11 | ) 12 | 13 | // OID is used to store a variable-length field with a one-octet size 14 | // prefix. See https://tools.ietf.org/html/rfc6637#section-9. 15 | type OID struct { 16 | bytes []byte 17 | } 18 | 19 | const ( 20 | // maxOID is the maximum number of bytes in a OID. 21 | maxOID = 254 22 | // reservedOIDLength1 and reservedOIDLength2 are OID lengths that the RFC 23 | // specifies are reserved. 24 | reservedOIDLength1 = 0 25 | reservedOIDLength2 = 0xff 26 | ) 27 | 28 | // NewOID returns a OID initialized with bytes. 29 | func NewOID(bytes []byte) *OID { 30 | switch len(bytes) { 31 | case reservedOIDLength1, reservedOIDLength2: 32 | panic("encoding: NewOID argument length is reserved") 33 | default: 34 | if len(bytes) > maxOID { 35 | panic("encoding: NewOID argument too large") 36 | } 37 | } 38 | 39 | return &OID{ 40 | bytes: bytes, 41 | } 42 | } 43 | 44 | // Bytes returns the decoded data. 45 | func (o *OID) Bytes() []byte { 46 | return o.bytes 47 | } 48 | 49 | // BitLength is the size in bits of the decoded data. 50 | func (o *OID) BitLength() uint16 { 51 | return uint16(len(o.bytes) * 8) 52 | } 53 | 54 | // EncodedBytes returns the encoded data. 55 | func (o *OID) EncodedBytes() []byte { 56 | return append([]byte{byte(len(o.bytes))}, o.bytes...) 57 | } 58 | 59 | // EncodedLength is the size in bytes of the encoded data. 60 | func (o *OID) EncodedLength() uint16 { 61 | return uint16(1 + len(o.bytes)) 62 | } 63 | 64 | // ReadFrom reads into b the next OID from r. 65 | func (o *OID) ReadFrom(r io.Reader) (int64, error) { 66 | var buf [1]byte 67 | n, err := io.ReadFull(r, buf[:]) 68 | if err != nil { 69 | if err == io.EOF { 70 | err = io.ErrUnexpectedEOF 71 | } 72 | return int64(n), err 73 | } 74 | 75 | switch buf[0] { 76 | case reservedOIDLength1, reservedOIDLength2: 77 | return int64(n), errors.UnsupportedError("reserved for future extensions") 78 | } 79 | 80 | o.bytes = make([]byte, buf[0]) 81 | 82 | nn, err := io.ReadFull(r, o.bytes) 83 | if err == io.EOF { 84 | err = io.ErrUnexpectedEOF 85 | } 86 | 87 | return int64(n) + int64(nn), err 88 | } 89 | -------------------------------------------------------------------------------- /brainpool/rcurve.go: -------------------------------------------------------------------------------- 1 | package brainpool 2 | 3 | import ( 4 | "crypto/elliptic" 5 | "math/big" 6 | ) 7 | 8 | var _ elliptic.Curve = (*rcurve)(nil) 9 | 10 | type rcurve struct { 11 | twisted elliptic.Curve 12 | params *elliptic.CurveParams 13 | z *big.Int 14 | zinv *big.Int 15 | z2 *big.Int 16 | z3 *big.Int 17 | zinv2 *big.Int 18 | zinv3 *big.Int 19 | } 20 | 21 | var ( 22 | two = big.NewInt(2) 23 | three = big.NewInt(3) 24 | ) 25 | 26 | func newrcurve(twisted elliptic.Curve, params *elliptic.CurveParams, z *big.Int) *rcurve { 27 | zinv := new(big.Int).ModInverse(z, params.P) 28 | return &rcurve{ 29 | twisted: twisted, 30 | params: params, 31 | z: z, 32 | zinv: zinv, 33 | z2: new(big.Int).Exp(z, two, params.P), 34 | z3: new(big.Int).Exp(z, three, params.P), 35 | zinv2: new(big.Int).Exp(zinv, two, params.P), 36 | zinv3: new(big.Int).Exp(zinv, three, params.P), 37 | } 38 | } 39 | 40 | func (curve *rcurve) toTwisted(x, y *big.Int) (*big.Int, *big.Int) { 41 | var tx, ty big.Int 42 | tx.Mul(x, curve.z2) 43 | tx.Mod(&tx, curve.params.P) 44 | ty.Mul(y, curve.z3) 45 | ty.Mod(&ty, curve.params.P) 46 | return &tx, &ty 47 | } 48 | 49 | func (curve *rcurve) fromTwisted(tx, ty *big.Int) (*big.Int, *big.Int) { 50 | var x, y big.Int 51 | x.Mul(tx, curve.zinv2) 52 | x.Mod(&x, curve.params.P) 53 | y.Mul(ty, curve.zinv3) 54 | y.Mod(&y, curve.params.P) 55 | return &x, &y 56 | } 57 | 58 | func (curve *rcurve) Params() *elliptic.CurveParams { 59 | return curve.params 60 | } 61 | 62 | func (curve *rcurve) IsOnCurve(x, y *big.Int) bool { 63 | return curve.twisted.IsOnCurve(curve.toTwisted(x, y)) 64 | } 65 | 66 | func (curve *rcurve) Add(x1, y1, x2, y2 *big.Int) (x, y *big.Int) { 67 | tx1, ty1 := curve.toTwisted(x1, y1) 68 | tx2, ty2 := curve.toTwisted(x2, y2) 69 | return curve.fromTwisted(curve.twisted.Add(tx1, ty1, tx2, ty2)) 70 | } 71 | 72 | func (curve *rcurve) Double(x1, y1 *big.Int) (x, y *big.Int) { 73 | return curve.fromTwisted(curve.twisted.Double(curve.toTwisted(x1, y1))) 74 | } 75 | 76 | func (curve *rcurve) ScalarMult(x1, y1 *big.Int, scalar []byte) (x, y *big.Int) { 77 | tx1, ty1 := curve.toTwisted(x1, y1) 78 | return curve.fromTwisted(curve.twisted.ScalarMult(tx1, ty1, scalar)) 79 | } 80 | 81 | func (curve *rcurve) ScalarBaseMult(scalar []byte) (x, y *big.Int) { 82 | return curve.fromTwisted(curve.twisted.ScalarBaseMult(scalar)) 83 | } 84 | -------------------------------------------------------------------------------- /openpgp/internal/encoding/mpi.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package encoding 6 | 7 | import ( 8 | "io" 9 | "math/big" 10 | "math/bits" 11 | ) 12 | 13 | // An MPI is used to store the contents of a big integer, along with the bit 14 | // length that was specified in the original input. This allows the MPI to be 15 | // reserialized exactly. 16 | type MPI struct { 17 | bytes []byte 18 | bitLength uint16 19 | } 20 | 21 | // NewMPI returns a MPI initialized with bytes. 22 | func NewMPI(bytes []byte) *MPI { 23 | for len(bytes) != 0 && bytes[0] == 0 { 24 | bytes = bytes[1:] 25 | } 26 | if len(bytes) == 0 { 27 | bitLength := uint16(0) 28 | return &MPI{bytes, bitLength} 29 | } 30 | bitLength := 8*uint16(len(bytes)-1) + uint16(bits.Len8(bytes[0])) 31 | return &MPI{bytes, bitLength} 32 | } 33 | 34 | // Bytes returns the decoded data. 35 | func (m *MPI) Bytes() []byte { 36 | return m.bytes 37 | } 38 | 39 | // BitLength is the size in bits of the decoded data. 40 | func (m *MPI) BitLength() uint16 { 41 | return m.bitLength 42 | } 43 | 44 | // EncodedBytes returns the encoded data. 45 | func (m *MPI) EncodedBytes() []byte { 46 | return append([]byte{byte(m.bitLength >> 8), byte(m.bitLength)}, m.bytes...) 47 | } 48 | 49 | // EncodedLength is the size in bytes of the encoded data. 50 | func (m *MPI) EncodedLength() uint16 { 51 | return uint16(2 + len(m.bytes)) 52 | } 53 | 54 | // ReadFrom reads into m the next MPI from r. 55 | func (m *MPI) ReadFrom(r io.Reader) (int64, error) { 56 | var buf [2]byte 57 | n, err := io.ReadFull(r, buf[0:]) 58 | if err != nil { 59 | if err == io.EOF { 60 | err = io.ErrUnexpectedEOF 61 | } 62 | return int64(n), err 63 | } 64 | 65 | m.bitLength = uint16(buf[0])<<8 | uint16(buf[1]) 66 | m.bytes = make([]byte, (int(m.bitLength)+7)/8) 67 | 68 | nn, err := io.ReadFull(r, m.bytes) 69 | if err == io.EOF { 70 | err = io.ErrUnexpectedEOF 71 | } 72 | 73 | // remove leading zero bytes from malformed GnuPG encoded MPIs: 74 | // https://bugs.gnupg.org/gnupg/issue1853 75 | // for _, b := range m.bytes { 76 | // if b != 0 { 77 | // break 78 | // } 79 | // m.bytes = m.bytes[1:] 80 | // m.bitLength -= 8 81 | // } 82 | 83 | return int64(n) + int64(nn), err 84 | } 85 | 86 | // SetBig initializes m with the bits from n. 87 | func (m *MPI) SetBig(n *big.Int) *MPI { 88 | m.bytes = n.Bytes() 89 | m.bitLength = uint16(n.BitLen()) 90 | return m 91 | } 92 | -------------------------------------------------------------------------------- /openpgp/ecdsa/ecdsa_test.go: -------------------------------------------------------------------------------- 1 | // Package ecdsa implements ECDSA signature, suitable for OpenPGP, 2 | // as specified in RFC 6637, section 5. 3 | package ecdsa 4 | 5 | import ( 6 | "crypto/rand" 7 | "io" 8 | "math/big" 9 | "testing" 10 | 11 | "github.com/ProtonMail/go-crypto/openpgp/internal/ecc" 12 | ) 13 | 14 | func TestCurves(t *testing.T) { 15 | for _, curve := range ecc.Curves { 16 | ECDSACurve, ok := curve.Curve.(ecc.ECDSACurve) 17 | if !ok { 18 | continue 19 | } 20 | 21 | t.Run(ECDSACurve.GetCurveName(), func(t *testing.T) { 22 | testFingerprint := make([]byte, 20) 23 | _, err := io.ReadFull(rand.Reader, testFingerprint[:]) 24 | if err != nil { 25 | t.Fatal(err) 26 | } 27 | 28 | priv := testGenerate(t, ECDSACurve) 29 | testSignVerify(t, priv) 30 | testValidation(t, priv) 31 | 32 | // Needs fresh key 33 | priv = testGenerate(t, ECDSACurve) 34 | testMarshalUnmarshal(t, priv) 35 | }) 36 | } 37 | } 38 | 39 | func testGenerate(t *testing.T, curve ecc.ECDSACurve) *PrivateKey { 40 | priv, err := GenerateKey(rand.Reader, curve) 41 | if err != nil { 42 | t.Fatal(err) 43 | } 44 | 45 | return priv 46 | } 47 | 48 | func testSignVerify(t *testing.T, priv *PrivateKey) { 49 | digest := make([]byte, 32) 50 | _, err := io.ReadFull(rand.Reader, digest[:]) 51 | if err != nil { 52 | t.Fatal(err) 53 | } 54 | 55 | r, s, err := Sign(rand.Reader, priv, digest) 56 | if err != nil { 57 | t.Errorf("error signing: %s", err) 58 | } 59 | 60 | result := Verify(&priv.PublicKey, digest, r, s) 61 | 62 | if !result { 63 | t.Error("unable to verify message") 64 | } 65 | } 66 | 67 | func testValidation(t *testing.T, priv *PrivateKey) { 68 | if err := Validate(priv); err != nil { 69 | t.Fatalf("valid key marked as invalid: %s", err) 70 | } 71 | 72 | priv.X.Sub(priv.X, big.NewInt(1)) 73 | if err := Validate(priv); err == nil { 74 | t.Fatal("failed to detect invalid key") 75 | } 76 | } 77 | 78 | func testMarshalUnmarshal(t *testing.T, priv *PrivateKey) { 79 | p := priv.MarshalPoint() 80 | d := priv.MarshalIntegerSecret() 81 | 82 | parsed := NewPrivateKey(*NewPublicKey(priv.GetCurve())) 83 | 84 | if err := parsed.UnmarshalPoint(p); err != nil { 85 | t.Fatalf("unable to unmarshal point: %s", err) 86 | } 87 | 88 | if err := parsed.UnmarshalIntegerSecret(d); err != nil { 89 | t.Fatalf("unable to unmarshal integer: %s", err) 90 | } 91 | 92 | if priv.X.Cmp(parsed.X) != 0 || priv.Y.Cmp(parsed.Y) != 0 || priv.D.Cmp(parsed.D) != 0 { 93 | t.Fatal("failed to marshal/unmarshal correctly") 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /openpgp/eddsa/eddsa_test.go: -------------------------------------------------------------------------------- 1 | // Package eddsa implements EdDSA signature, suitable for OpenPGP, as specified in 2 | // https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-13.7 3 | package eddsa 4 | 5 | import ( 6 | "bytes" 7 | "crypto/rand" 8 | "io" 9 | "testing" 10 | 11 | "github.com/ProtonMail/go-crypto/openpgp/internal/ecc" 12 | ) 13 | 14 | func TestCurves(t *testing.T) { 15 | for _, curve := range ecc.Curves { 16 | EdDSACurve, ok := curve.Curve.(ecc.EdDSACurve) 17 | if !ok { 18 | continue 19 | } 20 | 21 | t.Run(EdDSACurve.GetCurveName(), func(t *testing.T) { 22 | testFingerprint := make([]byte, 20) 23 | _, err := io.ReadFull(rand.Reader, testFingerprint[:]) 24 | if err != nil { 25 | t.Fatal(err) 26 | } 27 | 28 | priv := testGenerate(t, EdDSACurve) 29 | testSignVerify(t, priv) 30 | testValidation(t, priv) 31 | testMarshalUnmarshal(t, priv) 32 | }) 33 | } 34 | } 35 | 36 | func testGenerate(t *testing.T, curve ecc.EdDSACurve) *PrivateKey { 37 | priv, err := GenerateKey(rand.Reader, curve) 38 | if err != nil { 39 | t.Fatal(err) 40 | } 41 | 42 | return priv 43 | } 44 | 45 | func testSignVerify(t *testing.T, priv *PrivateKey) { 46 | digest := make([]byte, 32) 47 | _, err := io.ReadFull(rand.Reader, digest[:]) 48 | if err != nil { 49 | t.Fatal(err) 50 | } 51 | 52 | r, s, err := Sign(priv, digest) 53 | if err != nil { 54 | t.Errorf("error signing: %s", err) 55 | } 56 | 57 | result := Verify(&priv.PublicKey, digest, r, s) 58 | 59 | if !result { 60 | t.Error("unable to verify message") 61 | } 62 | } 63 | 64 | func testValidation(t *testing.T, priv *PrivateKey) { 65 | if err := Validate(priv); err != nil { 66 | t.Fatalf("valid key marked as invalid: %s", err) 67 | } 68 | 69 | priv.D[5] ^= 1 70 | if err := Validate(priv); err == nil { 71 | t.Fatal("failed to detect invalid key") 72 | } 73 | } 74 | 75 | func testMarshalUnmarshal(t *testing.T, priv *PrivateKey) { 76 | // Test correct zero padding 77 | priv.X[0] = 0 78 | priv.D[0] = 0 79 | 80 | x := priv.MarshalPoint() 81 | d := priv.MarshalByteSecret() 82 | 83 | parsed := NewPrivateKey(*NewPublicKey(priv.GetCurve())) 84 | 85 | if err := parsed.UnmarshalPoint(x); err != nil { 86 | t.Fatalf("unable to unmarshal point: %s", err) 87 | } 88 | 89 | if err := parsed.UnmarshalByteSecret(d); err != nil { 90 | t.Fatalf("unable to unmarshal integer: %s", err) 91 | } 92 | 93 | if !bytes.Equal(priv.X, parsed.X) || !bytes.Equal(priv.D, parsed.D) { 94 | t.Fatal("failed to marshal/unmarshal correctly") 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /openpgp/v2/keys_v5_test.go: -------------------------------------------------------------------------------- 1 | package v2 2 | 3 | import ( 4 | "bytes" 5 | "strings" 6 | "testing" 7 | 8 | "github.com/ProtonMail/go-crypto/openpgp/packet" 9 | ) 10 | 11 | var foreignKeys = []string{ 12 | v5PrivKey, 13 | } 14 | 15 | func TestReadPrivateForeignV5Key(t *testing.T) { 16 | if packet.V5Disabled { 17 | t.Skip() 18 | } 19 | for _, str := range foreignKeys { 20 | kring, err := ReadArmoredKeyRing(strings.NewReader(str)) 21 | if err != nil { 22 | t.Fatal(err) 23 | } 24 | checkV5Key(t, kring[0]) 25 | } 26 | } 27 | 28 | func TestReadPrivateSerializeForeignV5Key(t *testing.T) { 29 | if packet.V5Disabled { 30 | t.Skip() 31 | } 32 | for _, str := range foreignKeys { 33 | el, err := ReadArmoredKeyRing(strings.NewReader(str)) 34 | if err != nil { 35 | t.Fatal(err) 36 | } 37 | checkSerializeRead(t, el[0]) 38 | } 39 | } 40 | 41 | func checkV5Key(t *testing.T, ent *Entity) { 42 | key := ent.PrimaryKey 43 | if key.Version != 5 { 44 | t.Errorf("wrong key version %d", key.Version) 45 | } 46 | if len(key.Fingerprint) != 32 { 47 | t.Errorf("Wrong fingerprint length: %d", len(key.Fingerprint)) 48 | } 49 | signatures := ent.Revocations 50 | for _, id := range ent.Identities { 51 | signatures = append(signatures, id.SelfCertifications...) 52 | } 53 | for _, sig := range signatures { 54 | if sig == nil { 55 | continue 56 | } 57 | if sig.Packet.Version != 5 { 58 | t.Errorf("wrong signature version %d", sig.Packet.Version) 59 | } 60 | fgptLen := len(sig.Packet.IssuerFingerprint) 61 | if fgptLen != 32 { 62 | t.Errorf("Wrong fingerprint length in signature: %d", fgptLen) 63 | } 64 | } 65 | } 66 | 67 | func checkSerializeRead(t *testing.T, e *Entity) { 68 | // Entity serialize 69 | serializedEntity := bytes.NewBuffer(nil) 70 | err := e.Serialize(serializedEntity) 71 | if err != nil { 72 | t.Fatal(err) 73 | } 74 | el, err := ReadKeyRing(serializedEntity) 75 | if err != nil { 76 | t.Fatal(err) 77 | } 78 | checkV5Key(t, el[0]) 79 | 80 | // Without signing 81 | serializedEntity = bytes.NewBuffer(nil) 82 | err = e.SerializePrivateWithoutSigning(serializedEntity, nil) 83 | if err != nil { 84 | t.Fatal(err) 85 | } 86 | el, err = ReadKeyRing(serializedEntity) 87 | if err != nil { 88 | t.Fatal(err) 89 | } 90 | checkV5Key(t, el[0]) 91 | 92 | // Private 93 | serializedEntity = bytes.NewBuffer(nil) 94 | err = e.SerializePrivate(serializedEntity, nil) 95 | if err != nil { 96 | t.Fatal(err) 97 | } 98 | el, err = ReadKeyRing(serializedEntity) 99 | if err != nil { 100 | t.Fatal(err) 101 | } 102 | checkV5Key(t, el[0]) 103 | } 104 | -------------------------------------------------------------------------------- /openpgp/internal/algorithm/cipher.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package algorithm 6 | 7 | import ( 8 | "crypto/aes" 9 | "crypto/cipher" 10 | "crypto/des" 11 | 12 | "golang.org/x/crypto/cast5" 13 | ) 14 | 15 | // Cipher is an official symmetric key cipher algorithm. See RFC 4880, 16 | // section 9.2. 17 | type Cipher interface { 18 | // Id returns the algorithm ID, as a byte, of the cipher. 19 | Id() uint8 20 | // KeySize returns the key size, in bytes, of the cipher. 21 | KeySize() int 22 | // BlockSize returns the block size, in bytes, of the cipher. 23 | BlockSize() int 24 | // New returns a fresh instance of the given cipher. 25 | New(key []byte) cipher.Block 26 | } 27 | 28 | // The following constants mirror the OpenPGP standard (RFC 4880). 29 | const ( 30 | TripleDES = CipherFunction(2) 31 | CAST5 = CipherFunction(3) 32 | AES128 = CipherFunction(7) 33 | AES192 = CipherFunction(8) 34 | AES256 = CipherFunction(9) 35 | ) 36 | 37 | // CipherById represents the different block ciphers specified for OpenPGP. See 38 | // http://www.iana.org/assignments/pgp-parameters/pgp-parameters.xhtml#pgp-parameters-13 39 | var CipherById = map[uint8]Cipher{ 40 | TripleDES.Id(): TripleDES, 41 | CAST5.Id(): CAST5, 42 | AES128.Id(): AES128, 43 | AES192.Id(): AES192, 44 | AES256.Id(): AES256, 45 | } 46 | 47 | type CipherFunction uint8 48 | 49 | // ID returns the algorithm Id, as a byte, of cipher. 50 | func (sk CipherFunction) Id() uint8 { 51 | return uint8(sk) 52 | } 53 | 54 | // KeySize returns the key size, in bytes, of cipher. 55 | func (cipher CipherFunction) KeySize() int { 56 | switch cipher { 57 | case CAST5: 58 | return cast5.KeySize 59 | case AES128: 60 | return 16 61 | case AES192, TripleDES: 62 | return 24 63 | case AES256: 64 | return 32 65 | } 66 | return 0 67 | } 68 | 69 | // BlockSize returns the block size, in bytes, of cipher. 70 | func (cipher CipherFunction) BlockSize() int { 71 | switch cipher { 72 | case TripleDES: 73 | return des.BlockSize 74 | case CAST5: 75 | return 8 76 | case AES128, AES192, AES256: 77 | return 16 78 | } 79 | return 0 80 | } 81 | 82 | // New returns a fresh instance of the given cipher. 83 | func (cipher CipherFunction) New(key []byte) (block cipher.Block) { 84 | var err error 85 | switch cipher { 86 | case TripleDES: 87 | block, err = des.NewTripleDESCipher(key) 88 | case CAST5: 89 | block, err = cast5.NewCipher(key) 90 | case AES128, AES192, AES256: 91 | block, err = aes.NewCipher(key) 92 | } 93 | if err != nil { 94 | panic(err.Error()) 95 | } 96 | return 97 | } 98 | -------------------------------------------------------------------------------- /openpgp/packet/userid_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package packet 6 | 7 | import ( 8 | "testing" 9 | ) 10 | 11 | var userIdTests = []struct { 12 | id string 13 | name, comment, email string 14 | }{ 15 | {"", "", "", ""}, 16 | {"John Smith", "John Smith", "", ""}, 17 | {"John Smith ()", "John Smith", "", ""}, 18 | {"John Smith () <>", "John Smith", "", ""}, 19 | {"(comment", "", "comment", ""}, 20 | {"(comment)", "", "comment", ""}, 21 | {" sdfk", "", "", "email"}, 23 | {" John Smith ( Comment ) asdkflj < email > lksdfj", "John Smith", "Comment", "email"}, 24 | {" John Smith < email > lksdfj", "John Smith", "", "email"}, 25 | {"("}, 51 | {"foo", "bar", "", "foo (bar)"}, 52 | {"foo", "", "baz", "foo "}, 53 | {"", "bar", "baz", "(bar) "}, 54 | {"foo", "bar", "baz", "foo (bar) "}, 55 | } 56 | 57 | func TestNewUserId(t *testing.T) { 58 | for i, test := range newUserIdTests { 59 | uid := NewUserId(test.name, test.comment, test.email) 60 | if uid == nil { 61 | t.Errorf("#%d: returned nil", i) 62 | continue 63 | } 64 | if uid.Id != test.id { 65 | t.Errorf("#%d: got '%s', want '%s'", i, uid.Id, test.id) 66 | } 67 | } 68 | } 69 | 70 | var invalidNewUserIdTests = []struct { 71 | name, comment, email string 72 | }{ 73 | {"foo(", "", ""}, 74 | {"foo<", "", ""}, 75 | {"", "bar)", ""}, 76 | {"", "bar<", ""}, 77 | {"", "", "baz>"}, 78 | {"", "", "baz)"}, 79 | {"", "", "baz\x00"}, 80 | } 81 | 82 | func TestNewUserIdWithInvalidInput(t *testing.T) { 83 | for i, test := range invalidNewUserIdTests { 84 | if uid := NewUserId(test.name, test.comment, test.email); uid != nil { 85 | t.Errorf("#%d: returned non-nil value: %#v", i, uid) 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /openpgp/keys_v5_test.go: -------------------------------------------------------------------------------- 1 | package openpgp 2 | 3 | import ( 4 | "bytes" 5 | "strings" 6 | "testing" 7 | 8 | "github.com/ProtonMail/go-crypto/openpgp/packet" 9 | ) 10 | 11 | var foreignKeys = []string{ 12 | v5PrivKey, 13 | } 14 | 15 | func TestReadPrivateForeignV5Key(t *testing.T) { 16 | if packet.V5Disabled { 17 | t.Skip() 18 | } 19 | for _, str := range foreignKeys { 20 | kring, err := ReadArmoredKeyRing(strings.NewReader(str)) 21 | if err != nil { 22 | t.Fatal(err) 23 | } 24 | checkV5Key(t, kring[0]) 25 | } 26 | } 27 | 28 | func TestReadPrivateSerializeForeignV5Key(t *testing.T) { 29 | if packet.V5Disabled { 30 | t.Skip() 31 | } 32 | for _, str := range foreignKeys { 33 | el, err := ReadArmoredKeyRing(strings.NewReader(str)) 34 | if err != nil { 35 | t.Fatal(err) 36 | } 37 | checkSerializeRead(t, el[0]) 38 | } 39 | } 40 | 41 | func checkV5Key(t *testing.T, ent *Entity) { 42 | key := ent.PrimaryKey 43 | if key.Version != 5 { 44 | t.Errorf("wrong key version %d", key.Version) 45 | } 46 | if len(key.Fingerprint) != 32 { 47 | t.Errorf("Wrong fingerprint length: %d", len(key.Fingerprint)) 48 | } 49 | signatures := ent.Revocations 50 | for _, id := range ent.Identities { 51 | signatures = append(signatures, id.SelfSignature) 52 | signatures = append(signatures, id.Signatures...) 53 | } 54 | for _, sig := range signatures { 55 | if sig == nil { 56 | continue 57 | } 58 | if sig.Version != 5 { 59 | t.Errorf("wrong signature version %d", sig.Version) 60 | } 61 | fgptLen := len(sig.IssuerFingerprint) 62 | if fgptLen != 32 { 63 | t.Errorf("Wrong fingerprint length in signature: %d", fgptLen) 64 | } 65 | } 66 | } 67 | 68 | func checkSerializeRead(t *testing.T, e *Entity) { 69 | // Entity serialize 70 | serializedEntity := bytes.NewBuffer(nil) 71 | err := e.Serialize(serializedEntity) 72 | if err != nil { 73 | t.Fatal(err) 74 | } 75 | el, err := ReadKeyRing(serializedEntity) 76 | if err != nil { 77 | t.Fatal(err) 78 | } 79 | checkV5Key(t, el[0]) 80 | 81 | // Without signing 82 | serializedEntity = bytes.NewBuffer(nil) 83 | err = e.SerializePrivateWithoutSigning(serializedEntity, nil) 84 | if err != nil { 85 | t.Fatal(err) 86 | } 87 | el, err = ReadKeyRing(serializedEntity) 88 | if err != nil { 89 | t.Fatal(err) 90 | } 91 | checkV5Key(t, el[0]) 92 | 93 | // Private 94 | serializedEntity = bytes.NewBuffer(nil) 95 | err = e.SerializePrivate(serializedEntity, nil) 96 | if err != nil { 97 | t.Fatal(err) 98 | } 99 | el, err = ReadKeyRing(serializedEntity) 100 | if err != nil { 101 | t.Fatal(err) 102 | } 103 | checkV5Key(t, el[0]) 104 | } 105 | -------------------------------------------------------------------------------- /openpgp/packet/opaque_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package packet 6 | 7 | import ( 8 | "bytes" 9 | "encoding/hex" 10 | "io" 11 | "testing" 12 | ) 13 | 14 | // Test packet.Read error handling in OpaquePacket.Parse, 15 | // which attempts to re-read an OpaquePacket as a supported 16 | // Packet type. 17 | func TestOpaqueParseReason(t *testing.T) { 18 | buf, err := hex.DecodeString(UnsupportedKeyHex) 19 | if err != nil { 20 | t.Fatal(err) 21 | } 22 | or := NewOpaqueReader(bytes.NewBuffer(buf)) 23 | count := 0 24 | badPackets := 0 25 | var uid *UserId 26 | for { 27 | op, err := or.Next() 28 | if err == io.EOF { 29 | break 30 | } else if err != nil { 31 | t.Errorf("#%d: opaque read error: %v", count, err) 32 | break 33 | } 34 | // try to parse opaque packet 35 | p, _ := op.Parse() 36 | switch pkt := p.(type) { 37 | case *UserId: 38 | uid = pkt 39 | case *OpaquePacket: 40 | // If an OpaquePacket can't re-parse, packet.Read 41 | // certainly had its reasons. 42 | if pkt.Reason == nil { 43 | t.Errorf("#%d: opaque packet, no reason", count) 44 | } else { 45 | badPackets++ 46 | } 47 | } 48 | count++ 49 | } 50 | 51 | const expectedBad = 3 52 | // Test post-conditions, make sure we actually parsed packets as expected. 53 | if badPackets != expectedBad { 54 | t.Errorf("unexpected # unparseable packets: %d (want %d)", badPackets, expectedBad) 55 | } 56 | if uid == nil { 57 | t.Errorf("failed to find expected UID in unsupported keyring") 58 | } else if uid.Id != "Armin M. Warda " { 59 | t.Errorf("unexpected UID: %v", uid.Id) 60 | } 61 | } 62 | 63 | // This key material has public key and signature packet versions modified to 64 | // an unsupported value (1), so that trying to parse the OpaquePacket to 65 | // a typed packet will get an error. It also contains a GnuPG trust packet. 66 | // (Created with: od -An -t x1 pubring.gpg | xargs | sed 's/ //g') 67 | const UnsupportedKeyHex = `988d012e7a18a20000010400d6ac00d92b89c1f4396c243abb9b76d2e9673ad63483291fed88e22b82e255e441c078c6abbbf7d2d195e50b62eeaa915b85b0ec20c225ce2c64c167cacb6e711daf2e45da4a8356a059b8160e3b3628ac0dd8437b31f06d53d6e8ea4214d4a26406a6b63e1001406ef23e0bb3069fac9a99a91f77dfafd5de0f188a5da5e3c9000511b42741726d696e204d2e205761726461203c7761726461406e657068696c696d2e727568722e64653e8900950105102e8936c705d1eb399e58489901013f0e03ff5a0c4f421e34fcfa388129166420c08cd76987bcdec6f01bd0271459a85cc22048820dd4e44ac2c7d23908d540f54facf1b36b0d9c20488781ce9dca856531e76e2e846826e9951338020a03a09b57aa5faa82e9267458bd76105399885ac35af7dc1cbb6aaed7c39e1039f3b5beda2c0e916bd38560509bab81235d1a0ead83b0020000` 68 | -------------------------------------------------------------------------------- /internal/byteutil/byteutil.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 ProtonTech AG 2 | // This file contains necessary tools for the aex and ocb packages. 3 | // 4 | // These functions SHOULD NOT be used elsewhere, since they are optimized for 5 | // specific input nature in the EAX and OCB modes of operation. 6 | 7 | package byteutil 8 | 9 | // GfnDouble computes 2 * input in the field of 2^n elements. 10 | // The irreducible polynomial in the finite field for n=128 is 11 | // x^128 + x^7 + x^2 + x + 1 (equals 0x87) 12 | // Constant-time execution in order to avoid side-channel attacks 13 | func GfnDouble(input []byte) []byte { 14 | if len(input) != 16 { 15 | panic("Doubling in GFn only implemented for n = 128") 16 | } 17 | // If the first bit is zero, return 2L = L << 1 18 | // Else return (L << 1) xor 0^120 10000111 19 | shifted := ShiftBytesLeft(input) 20 | shifted[15] ^= ((input[0] >> 7) * 0x87) 21 | return shifted 22 | } 23 | 24 | // ShiftBytesLeft outputs the byte array corresponding to x << 1 in binary. 25 | func ShiftBytesLeft(x []byte) []byte { 26 | l := len(x) 27 | dst := make([]byte, l) 28 | for i := 0; i < l-1; i++ { 29 | dst[i] = (x[i] << 1) | (x[i+1] >> 7) 30 | } 31 | dst[l-1] = x[l-1] << 1 32 | return dst 33 | } 34 | 35 | // ShiftNBytesLeft puts in dst the byte array corresponding to x << n in binary. 36 | func ShiftNBytesLeft(dst, x []byte, n int) { 37 | // Erase first n / 8 bytes 38 | copy(dst, x[n/8:]) 39 | 40 | // Shift the remaining n % 8 bits 41 | bits := uint(n % 8) 42 | l := len(dst) 43 | for i := 0; i < l-1; i++ { 44 | dst[i] = (dst[i] << bits) | (dst[i+1] >> uint(8-bits)) 45 | } 46 | dst[l-1] = dst[l-1] << bits 47 | 48 | // Append trailing zeroes 49 | dst = append(dst, make([]byte, n/8)...) 50 | } 51 | 52 | // XorBytesMut replaces X with X XOR Y. len(X) must be >= len(Y). 53 | func XorBytesMut(X, Y []byte) { 54 | for i := 0; i < len(Y); i++ { 55 | X[i] ^= Y[i] 56 | } 57 | } 58 | 59 | // XorBytes puts X XOR Y into Z. len(Z) and len(X) must be >= len(Y). 60 | func XorBytes(Z, X, Y []byte) { 61 | for i := 0; i < len(Y); i++ { 62 | Z[i] = X[i] ^ Y[i] 63 | } 64 | } 65 | 66 | // RightXor XORs smaller input (assumed Y) at the right of the larger input (assumed X) 67 | func RightXor(X, Y []byte) []byte { 68 | offset := len(X) - len(Y) 69 | xored := make([]byte, len(X)) 70 | copy(xored, X) 71 | for i := 0; i < len(Y); i++ { 72 | xored[offset+i] ^= Y[i] 73 | } 74 | return xored 75 | } 76 | 77 | // SliceForAppend takes a slice and a requested number of bytes. It returns a 78 | // slice with the contents of the given slice followed by that many bytes and a 79 | // second slice that aliases into it and contains only the extra bytes. If the 80 | // original slice has sufficient capacity then no allocation is performed. 81 | func SliceForAppend(in []byte, n int) (head, tail []byte) { 82 | if total := len(in) + n; cap(in) >= total { 83 | head = in[:total] 84 | } else { 85 | head = make([]byte, total) 86 | copy(head, in) 87 | } 88 | tail = head[len(in):] 89 | return 90 | } 91 | -------------------------------------------------------------------------------- /openpgp/packet/symmetric_key_encrypted_data_test.go: -------------------------------------------------------------------------------- 1 | package packet 2 | 3 | // These test vectors contain V4 or V5 symmetric key encrypted packets followed 4 | // by an integrity protected packet (SEIPD v1 or v2). 5 | 6 | type packetSequence struct { 7 | password string 8 | packets string 9 | contents string 10 | faultyDataPacket string 11 | } 12 | 13 | func keyAndIpePackets() []*packetSequence { 14 | if V5Disabled { 15 | return []*packetSequence{symEncRFC9580, symEncRFC4880} 16 | } 17 | return []*packetSequence{symEncRFC9580, symEncRFC4880, aeadEaxRFC, aeadOcbRFC} 18 | } 19 | 20 | // https://www.ietf.org/archive/id/draft-koch-openpgp-2015-rfc4880bis-00.html#name-complete-aead-eax-encrypted- 21 | var aeadEaxRFC = &packetSequence{ 22 | password: "password", 23 | packets: "c33e0507010308cd5a9f70fbe0bc6590bc669e34e500dcaedc5b32aa2dab02359dee19d07c3446c4312a34ae1967a2fb7e928ea5b4fa8012bd456d1738c63c36d44a0107010eb732379f73c4928de25facfe6517ec105dc11a81dc0cb8a2f6f3d90016384a56fc821ae11ae8dbcb49862655dea88d06a81486801b0ff387bd2eab013de1259586906eab2476", 24 | contents: "cb1462000000000048656c6c6f2c20776f726c64210a", 25 | } 26 | 27 | // https://www.ietf.org/archive/id/draft-koch-openpgp-2015-rfc4880bis-00.html#name-complete-aead-ocb-encrypted- 28 | var aeadOcbRFC = &packetSequence{ 29 | password: "password", 30 | packets: "c33d05070203089f0b7da3e5ea64779099e326e5400a90936cefb4e8eba08c6773716d1f2714540a38fcac529949dac529d3de31e15b4aeb729e330033dbedd4490107020e5ed2bc1e470abe8f1d644c7a6c8a567b0f7701196611a154ba9c2574cd056284a8ef68035c623d93cc708a43211bb6eaf2b27f7c18d571bcd83b20add3a08b73af15b9a098", 31 | contents: "cb1462000000000048656c6c6f2c20776f726c64210a", 32 | } 33 | 34 | // From OpenPGP RFC9580 A.9 https://www.rfc-editor.org/rfc/rfc9580.html#name-sample-aead-eax-encryption- 35 | var symEncRFC9580 = &packetSequence{ 36 | password: "password", 37 | packets: "c340061e07010b0308a5ae579d1fc5d82bff69224f919993b3506fa3b59a6a73cff8c5efc5f41c57fb54e1c226815d7828f5f92c454eb65ebe00ab5986c68e6e7c55d269020701069ff90e3b321964f3a42913c8dcc6619325015227efb7eaeaa49f04c2e674175d4a3d226ed6afcb9ca9ac122c1470e11c63d4c0ab241c6a938ad48bf99a5a99b90bba8325de61047540258ab7959a95ad051dda96eb15431dfef5f5e2255ca78261546e339a", 38 | contents: "cb1362000000000048656c6c6f2c20776f726c6421d50eae5bf0cd6705500355816cb0c8ff", 39 | // Missing last authentication chunk 40 | faultyDataPacket: "d259020701069ff90e3b321964f3a42913c8dcc6619325015227efb7eaeaa49f04c2e674175d4a3d226ed6afcb9ca9ac122c1470e11c63d4c0ab241c6a938ad48bf99a5a99b90bba8325de61047540258ab7959a95ad051dda96eb", 41 | } 42 | 43 | // From the OpenPGP interoperability test suite (Test: S2K mechanisms, iterated min + esk) 44 | var symEncRFC4880 = &packetSequence{ 45 | password: "password", 46 | packets: "c32e0409030873616c7a6967657200080674a0d96a4a6e122b1d5bbaa3fac117b9cbb46c7e38f12967386b57e2f79d11d23f01cee77ceed8544e6d52c78bd33c81bd366c8673b68955ddbd1ade98fe6a9b4e27ae54cd10dda7cd3a4637f44e0ead895ebebdcf0c679f1342745628f104e7", 47 | contents: "cb1462000000000048656c6c6f20576f726c64203a29", 48 | } 49 | -------------------------------------------------------------------------------- /openpgp/v2/canonical_text.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package v2 6 | 7 | import ( 8 | "bytes" 9 | "hash" 10 | "io" 11 | ) 12 | 13 | // NewCanonicalTextHash reformats text written to it into the canonical 14 | // form and then applies the hash h. See RFC 4880, section 5.2.1. 15 | func NewCanonicalTextHash(h hash.Hash) hash.Hash { 16 | return &canonicalTextHash{h, 0} 17 | } 18 | 19 | // NewCanonicalTextWriteCloser reformats text written to it into the canonical 20 | // form. See RFC 4880, section 5.2.1. 21 | func NewCanonicalTextWriteCloser(w io.WriteCloser) io.WriteCloser { 22 | return &canonicalTextWriteCloser{w, 0} 23 | } 24 | 25 | // NewCanonicalTextReader reformats text read from it into the canonical 26 | // form. See RFC 4880, section 5.2.1. 27 | func NewCanonicalTextReader(r io.Reader) io.Reader { 28 | return &canonicalTextReader{r, bytes.NewBuffer(nil), 0} 29 | } 30 | 31 | type canonicalTextHash struct { 32 | h hash.Hash 33 | s int 34 | } 35 | 36 | var newline = []byte{'\r', '\n'} 37 | 38 | func writeCanonical(cw io.Writer, buf []byte, s *int) (int, error) { 39 | start := 0 40 | for i, c := range buf { 41 | switch *s { 42 | case 0: 43 | if c == '\r' { 44 | *s = 1 45 | } else if c == '\n' { 46 | if _, err := cw.Write(buf[start:i]); err != nil { 47 | return 0, err 48 | } 49 | if _, err := cw.Write(newline); err != nil { 50 | return 0, err 51 | } 52 | start = i + 1 53 | } 54 | case 1: 55 | *s = 0 56 | } 57 | } 58 | 59 | if _, err := cw.Write(buf[start:]); err != nil { 60 | return 0, err 61 | } 62 | return len(buf), nil 63 | } 64 | 65 | func (cth *canonicalTextHash) Write(buf []byte) (int, error) { 66 | return writeCanonical(cth.h, buf, &cth.s) 67 | } 68 | 69 | func (cth *canonicalTextHash) Sum(in []byte) []byte { 70 | return cth.h.Sum(in) 71 | } 72 | 73 | func (cth *canonicalTextHash) Reset() { 74 | cth.h.Reset() 75 | cth.s = 0 76 | } 77 | 78 | func (cth *canonicalTextHash) Size() int { 79 | return cth.h.Size() 80 | } 81 | 82 | func (cth *canonicalTextHash) BlockSize() int { 83 | return cth.h.BlockSize() 84 | } 85 | 86 | type canonicalTextWriteCloser struct { 87 | w io.WriteCloser 88 | s int 89 | } 90 | 91 | func (tw *canonicalTextWriteCloser) Write(buf []byte) (int, error) { 92 | return writeCanonical(tw.w, buf, &tw.s) 93 | } 94 | 95 | func (tw *canonicalTextWriteCloser) Close() error { 96 | return tw.w.Close() 97 | } 98 | 99 | type canonicalTextReader struct { 100 | r io.Reader 101 | buffer *bytes.Buffer 102 | s int 103 | } 104 | 105 | func (tr *canonicalTextReader) Read(buf []byte) (int, error) { 106 | if tr.buffer.Len() > 0 { 107 | return tr.buffer.Read(buf) 108 | } 109 | n, err := tr.r.Read(buf) 110 | if err != nil { 111 | return n, err 112 | } 113 | if _, err = writeCanonical(tr.buffer, buf[:n], &tr.s); err != nil { 114 | return n, err 115 | } 116 | return tr.buffer.Read(buf) 117 | } 118 | -------------------------------------------------------------------------------- /openpgp/packet/userattribute.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package packet 6 | 7 | import ( 8 | "bytes" 9 | "image" 10 | "image/jpeg" 11 | "io" 12 | ) 13 | 14 | const UserAttrImageSubpacket = 1 15 | 16 | // UserAttribute is capable of storing other types of data about a user 17 | // beyond name, email and a text comment. In practice, user attributes are typically used 18 | // to store a signed thumbnail photo JPEG image of the user. 19 | // See RFC 4880, section 5.12. 20 | type UserAttribute struct { 21 | Contents []*OpaqueSubpacket 22 | } 23 | 24 | // NewUserAttributePhoto creates a user attribute packet 25 | // containing the given images. 26 | func NewUserAttributePhoto(photos ...image.Image) (uat *UserAttribute, err error) { 27 | uat = new(UserAttribute) 28 | for _, photo := range photos { 29 | var buf bytes.Buffer 30 | // RFC 4880, Section 5.12.1. 31 | data := []byte{ 32 | 0x10, 0x00, // Little-endian image header length (16 bytes) 33 | 0x01, // Image header version 1 34 | 0x01, // JPEG 35 | 0, 0, 0, 0, // 12 reserved octets, must be all zero. 36 | 0, 0, 0, 0, 37 | 0, 0, 0, 0} 38 | if _, err = buf.Write(data); err != nil { 39 | return 40 | } 41 | if err = jpeg.Encode(&buf, photo, nil); err != nil { 42 | return 43 | } 44 | 45 | lengthBuf := make([]byte, 5) 46 | n := serializeSubpacketLength(lengthBuf, len(buf.Bytes())+1) 47 | lengthBuf = lengthBuf[:n] 48 | 49 | uat.Contents = append(uat.Contents, &OpaqueSubpacket{ 50 | SubType: UserAttrImageSubpacket, 51 | EncodedLength: lengthBuf, 52 | Contents: buf.Bytes(), 53 | }) 54 | } 55 | return 56 | } 57 | 58 | // NewUserAttribute creates a new user attribute packet containing the given subpackets. 59 | func NewUserAttribute(contents ...*OpaqueSubpacket) *UserAttribute { 60 | return &UserAttribute{Contents: contents} 61 | } 62 | 63 | func (uat *UserAttribute) parse(r io.Reader) (err error) { 64 | // RFC 4880, section 5.13 65 | b, err := io.ReadAll(r) 66 | if err != nil { 67 | return 68 | } 69 | uat.Contents, err = OpaqueSubpackets(b) 70 | return 71 | } 72 | 73 | // Serialize marshals the user attribute to w in the form of an OpenPGP packet, including 74 | // header. 75 | func (uat *UserAttribute) Serialize(w io.Writer) (err error) { 76 | var buf bytes.Buffer 77 | for _, sp := range uat.Contents { 78 | err = sp.Serialize(&buf) 79 | if err != nil { 80 | return err 81 | } 82 | } 83 | if err = serializeHeader(w, packetTypeUserAttribute, buf.Len()); err != nil { 84 | return err 85 | } 86 | _, err = w.Write(buf.Bytes()) 87 | return 88 | } 89 | 90 | // ImageData returns zero or more byte slices, each containing 91 | // JPEG File Interchange Format (JFIF), for each photo in the 92 | // user attribute packet. 93 | func (uat *UserAttribute) ImageData() (imageData [][]byte) { 94 | for _, sp := range uat.Contents { 95 | if sp.SubType == UserAttrImageSubpacket && len(sp.Contents) > 16 { 96 | imageData = append(imageData, sp.Contents[16:]) 97 | } 98 | } 99 | return 100 | } 101 | -------------------------------------------------------------------------------- /openpgp/internal/ecc/x448.go: -------------------------------------------------------------------------------- 1 | // Package ecc implements a generic interface for ECDH, ECDSA, and EdDSA. 2 | package ecc 3 | 4 | import ( 5 | "crypto/subtle" 6 | "io" 7 | 8 | "github.com/ProtonMail/go-crypto/openpgp/errors" 9 | x448lib "github.com/cloudflare/circl/dh/x448" 10 | ) 11 | 12 | type x448 struct{} 13 | 14 | func NewX448() *x448 { 15 | return &x448{} 16 | } 17 | 18 | func (c *x448) GetCurveName() string { 19 | return "x448" 20 | } 21 | 22 | // MarshalBytePoint encodes the public point from native format, adding the prefix. 23 | // See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-5.5.5.6 24 | func (c *x448) MarshalBytePoint(point []byte) []byte { 25 | return append([]byte{0x40}, point...) 26 | } 27 | 28 | // UnmarshalBytePoint decodes a point from prefixed format to native. 29 | // See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-5.5.5.6 30 | func (c *x448) UnmarshalBytePoint(point []byte) []byte { 31 | if len(point) != x448lib.Size+1 { 32 | return nil 33 | } 34 | 35 | return point[1:] 36 | } 37 | 38 | // MarshalByteSecret encoded a scalar from native format to prefixed. 39 | // See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-5.5.5.6.1.2 40 | func (c *x448) MarshalByteSecret(d []byte) []byte { 41 | return append([]byte{0x40}, d...) 42 | } 43 | 44 | // UnmarshalByteSecret decodes a scalar from prefixed format to native. 45 | // See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-5.5.5.6.1.2 46 | func (c *x448) UnmarshalByteSecret(d []byte) []byte { 47 | if len(d) != x448lib.Size+1 { 48 | return nil 49 | } 50 | 51 | // Store without prefix 52 | return d[1:] 53 | } 54 | 55 | func (c *x448) generateKeyPairBytes(rand io.Reader) (sk, pk x448lib.Key, err error) { 56 | if _, err = rand.Read(sk[:]); err != nil { 57 | return 58 | } 59 | 60 | x448lib.KeyGen(&pk, &sk) 61 | return 62 | } 63 | 64 | func (c *x448) GenerateECDH(rand io.Reader) (point []byte, secret []byte, err error) { 65 | priv, pub, err := c.generateKeyPairBytes(rand) 66 | if err != nil { 67 | return 68 | } 69 | 70 | return pub[:], priv[:], nil 71 | } 72 | 73 | func (c *x448) Encaps(rand io.Reader, point []byte) (ephemeral, sharedSecret []byte, err error) { 74 | var pk, ss x448lib.Key 75 | seed, e, err := c.generateKeyPairBytes(rand) 76 | if err != nil { 77 | return nil, nil, err 78 | } 79 | copy(pk[:], point) 80 | x448lib.Shared(&ss, &seed, &pk) 81 | 82 | return e[:], ss[:], nil 83 | } 84 | 85 | func (c *x448) Decaps(ephemeral, secret []byte) (sharedSecret []byte, err error) { 86 | var ss, sk, e x448lib.Key 87 | 88 | copy(sk[:], secret) 89 | copy(e[:], ephemeral) 90 | x448lib.Shared(&ss, &sk, &e) 91 | 92 | return ss[:], nil 93 | } 94 | 95 | func (c *x448) ValidateECDH(point []byte, secret []byte) error { 96 | var sk, pk, expectedPk x448lib.Key 97 | 98 | copy(pk[:], point) 99 | copy(sk[:], secret) 100 | x448lib.KeyGen(&expectedPk, &sk) 101 | 102 | if subtle.ConstantTimeCompare(expectedPk[:], pk[:]) == 0 { 103 | return errors.KeyInvalidError("ecc: invalid curve25519 public point") 104 | } 105 | 106 | return nil 107 | } 108 | -------------------------------------------------------------------------------- /openpgp/packet/private_key_test_data.go: -------------------------------------------------------------------------------- 1 | package packet 2 | 3 | // Generated with `gpg --export-secret-keys "Test Key 2"` 4 | const privKeyRSAHex = "9501fe044cc349a8010400b70ca0010e98c090008d45d1ee8f9113bd5861fd57b88bacb7c68658747663f1e1a3b5a98f32fda6472373c024b97359cd2efc88ff60f77751adfbf6af5e615e6a1408cfad8bf0cea30b0d5f53aa27ad59089ba9b15b7ebc2777a25d7b436144027e3bcd203909f147d0e332b240cf63d3395f5dfe0df0a6c04e8655af7eacdf0011010001fe0303024a252e7d475fd445607de39a265472aa74a9320ba2dac395faa687e9e0336aeb7e9a7397e511b5afd9dc84557c80ac0f3d4d7bfec5ae16f20d41c8c84a04552a33870b930420e230e179564f6d19bb153145e76c33ae993886c388832b0fa042ddda7f133924f3854481533e0ede31d51278c0519b29abc3bf53da673e13e3e1214b52413d179d7f66deee35cac8eacb060f78379d70ef4af8607e68131ff529439668fc39c9ce6dfef8a5ac234d234802cbfb749a26107db26406213ae5c06d4673253a3cbee1fcbae58d6ab77e38d6e2c0e7c6317c48e054edadb5a40d0d48acb44643d998139a8a66bb820be1f3f80185bc777d14b5954b60effe2448a036d565c6bc0b915fcea518acdd20ab07bc1529f561c58cd044f723109b93f6fd99f876ff891d64306b5d08f48bab59f38695e9109c4dec34013ba3153488ce070268381ba923ee1eb77125b36afcb4347ec3478c8f2735b06ef17351d872e577fa95d0c397c88c71b59629a36aec" 5 | 6 | // Generated by `gpg --export-secret-keys` followed by a manual extraction of 7 | // the ElGamal subkey from the packets. 8 | const privKeyElGamalHex = "9d0157044df9ee1a100400eb8e136a58ec39b582629cdadf830bc64e0a94ed8103ca8bb247b27b11b46d1d25297ef4bcc3071785ba0c0bedfe89eabc5287fcc0edf81ab5896c1c8e4b20d27d79813c7aede75320b33eaeeaa586edc00fd1036c10133e6ba0ff277245d0d59d04b2b3421b7244aca5f4a8d870c6f1c1fbff9e1c26699a860b9504f35ca1d700030503fd1ededd3b840795be6d9ccbe3c51ee42e2f39233c432b831ddd9c4e72b7025a819317e47bf94f9ee316d7273b05d5fcf2999c3a681f519b1234bbfa6d359b4752bd9c3f77d6b6456cde152464763414ca130f4e91d91041432f90620fec0e6d6b5116076c2985d5aeaae13be492b9b329efcaf7ee25120159a0a30cd976b42d7afe030302dae7eb80db744d4960c4df930d57e87fe81412eaace9f900e6c839817a614ddb75ba6603b9417c33ea7b6c93967dfa2bcff3fa3c74a5ce2c962db65b03aece14c96cbd0038fc" 9 | 10 | // pkcs1PrivKeyHex is a PKCS#1, RSA private key. 11 | // Generated by `openssl genrsa 1024 | openssl rsa -outform DER | xxd -p` 12 | const pkcs1PrivKeyHex = "3082025d02010002818100e98edfa1c3b35884a54d0b36a6a603b0290fa85e49e30fa23fc94fef9c6790bc4849928607aa48d809da326fb42a969d06ad756b98b9c1a90f5d4a2b6d0ac05953c97f4da3120164a21a679793ce181c906dc01d235cc085ddcdf6ea06c389b6ab8885dfd685959e693138856a68a7e5db263337ff82a088d583a897cf2d59e9020301000102818100b6d5c9eb70b02d5369b3ee5b520a14490b5bde8a317d36f7e4c74b7460141311d1e5067735f8f01d6f5908b2b96fbd881f7a1ab9a84d82753e39e19e2d36856be960d05ac9ef8e8782ea1b6d65aee28fdfe1d61451e8cff0adfe84322f12cf455028b581cf60eb9e0e140ba5d21aeba6c2634d7c65318b9a665fc01c3191ca21024100fa5e818da3705b0fa33278bb28d4b6f6050388af2d4b75ec9375dd91ccf2e7d7068086a8b82a8f6282e4fbbdb8a7f2622eb97295249d87acea7f5f816f54d347024100eecf9406d7dc49cdfb95ab1eff4064de84c7a30f64b2798936a0d2018ba9eb52e4b636f82e96c49cc63b80b675e91e40d1b2e4017d4b9adaf33ab3d9cf1c214f024100c173704ace742c082323066226a4655226819a85304c542b9dacbeacbf5d1881ee863485fcf6f59f3a604f9b42289282067447f2b13dfeed3eab7851fc81e0550240741fc41f3fc002b382eed8730e33c5d8de40256e4accee846667f536832f711ab1d4590e7db91a8a116ac5bff3be13d3f9243ff2e976662aa9b395d907f8e9c9024046a5696c9ef882363e06c9fa4e2f5b580906452befba03f4a99d0f873697ef1f851d2226ca7934b30b7c3e80cb634a67172bbbf4781735fe3e09263e2dd723e7" 13 | -------------------------------------------------------------------------------- /ocb/rfc7253_test_vectors_suite_a.go: -------------------------------------------------------------------------------- 1 | package ocb 2 | 3 | import ( 4 | "encoding/hex" 5 | ) 6 | 7 | // Test vectors from https://tools.ietf.org/html/rfc7253. Note that key is 8 | // shared across tests. 9 | var testKey, _ = hex.DecodeString("000102030405060708090A0B0C0D0E0F") 10 | 11 | var rfc7253testVectors = []struct { 12 | nonce, header, plaintext, ciphertext string 13 | }{ 14 | {"BBAA99887766554433221100", 15 | "", 16 | "", 17 | "785407BFFFC8AD9EDCC5520AC9111EE6"}, 18 | {"BBAA99887766554433221101", 19 | "0001020304050607", 20 | "0001020304050607", 21 | "6820B3657B6F615A5725BDA0D3B4EB3A257C9AF1F8F03009"}, 22 | {"BBAA99887766554433221102", 23 | "0001020304050607", 24 | "", 25 | "81017F8203F081277152FADE694A0A00"}, 26 | {"BBAA99887766554433221103", 27 | "", 28 | "0001020304050607", 29 | "45DD69F8F5AAE72414054CD1F35D82760B2CD00D2F99BFA9"}, 30 | {"BBAA99887766554433221104", 31 | "000102030405060708090A0B0C0D0E0F", 32 | "000102030405060708090A0B0C0D0E0F", 33 | "571D535B60B277188BE5147170A9A22C3AD7A4FF3835B8C5701C1CCEC8FC3358"}, 34 | {"BBAA99887766554433221105", 35 | "000102030405060708090A0B0C0D0E0F", 36 | "", 37 | "8CF761B6902EF764462AD86498CA6B97"}, 38 | {"BBAA99887766554433221106", 39 | "", 40 | "000102030405060708090A0B0C0D0E0F", 41 | "5CE88EC2E0692706A915C00AEB8B2396F40E1C743F52436BDF06D8FA1ECA343D"}, 42 | {"BBAA99887766554433221107", 43 | "000102030405060708090A0B0C0D0E0F1011121314151617", 44 | "000102030405060708090A0B0C0D0E0F1011121314151617", 45 | "1CA2207308C87C010756104D8840CE1952F09673A448A122C92C62241051F57356D7F3C90BB0E07F"}, 46 | {"BBAA99887766554433221108", 47 | "000102030405060708090A0B0C0D0E0F1011121314151617", 48 | "", 49 | "6DC225A071FC1B9F7C69F93B0F1E10DE"}, 50 | {"BBAA99887766554433221109", 51 | "", 52 | "000102030405060708090A0B0C0D0E0F1011121314151617", 53 | "221BD0DE7FA6FE993ECCD769460A0AF2D6CDED0C395B1C3CE725F32494B9F914D85C0B1EB38357FF"}, 54 | {"BBAA9988776655443322110A", 55 | "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F", 56 | "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F", 57 | "BD6F6C496201C69296C11EFD138A467ABD3C707924B964DEAFFC40319AF5A48540FBBA186C5553C68AD9F592A79A4240"}, 58 | {"BBAA9988776655443322110B", 59 | "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F", 60 | "", 61 | "FE80690BEE8A485D11F32965BC9D2A32"}, 62 | {"BBAA9988776655443322110C", 63 | "", 64 | "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F", 65 | "2942BFC773BDA23CABC6ACFD9BFD5835BD300F0973792EF46040C53F1432BCDFB5E1DDE3BC18A5F840B52E653444D5DF"}, 66 | {"BBAA9988776655443322110D", 67 | "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627", 68 | "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627", 69 | "D5CA91748410C1751FF8A2F618255B68A0A12E093FF454606E59F9C1D0DDC54B65E8628E568BAD7AED07BA06A4A69483A7035490C5769E60"}, 70 | {"BBAA9988776655443322110E", 71 | "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627", 72 | "", 73 | "C5CD9D1850C141E358649994EE701B68"}, 74 | {"BBAA9988776655443322110F", 75 | "", 76 | "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F2021222324252627", 77 | "4412923493C57D5DE0D700F753CCE0D1D2D95060122E9F15A5DDBFC5787E50B5CC55EE507BCB084E479AD363AC366B95A98CA5F3000B1479"}, 78 | } 79 | -------------------------------------------------------------------------------- /openpgp/ecdh/ecdh_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package ecdh implements ECDH encryption, suitable for OpenPGP, 6 | // as specified in RFC 6637, section 8. 7 | package ecdh 8 | 9 | import ( 10 | "bytes" 11 | "crypto/rand" 12 | "github.com/ProtonMail/go-crypto/openpgp/internal/ecc" 13 | "io" 14 | "testing" 15 | 16 | "github.com/ProtonMail/go-crypto/openpgp/internal/algorithm" 17 | ) 18 | 19 | func TestCurves(t *testing.T) { 20 | for _, curve := range ecc.Curves { 21 | ECDHCurve, ok := curve.Curve.(ecc.ECDHCurve) 22 | if !ok { 23 | continue 24 | } 25 | 26 | t.Run(ECDHCurve.GetCurveName(), func(t *testing.T) { 27 | testFingerprint := make([]byte, 20) 28 | _, err := io.ReadFull(rand.Reader, testFingerprint[:]) 29 | if err != nil { 30 | t.Fatal(err) 31 | } 32 | 33 | priv := testGenerate(t, ECDHCurve) 34 | testEncryptDecrypt(t, priv, curve.Oid.Bytes(), testFingerprint) 35 | testValidation(t, priv) 36 | 37 | // Needs fresh key 38 | priv = testGenerate(t, ECDHCurve) 39 | testMarshalUnmarshal(t, priv) 40 | }) 41 | } 42 | } 43 | 44 | func testGenerate(t *testing.T, curve ecc.ECDHCurve) *PrivateKey { 45 | kdf := KDF{ 46 | Hash: algorithm.SHA512, 47 | Cipher: algorithm.AES256, 48 | } 49 | 50 | priv, err := GenerateKey(rand.Reader, curve, kdf) 51 | if err != nil { 52 | t.Fatal(err) 53 | } 54 | 55 | return priv 56 | } 57 | 58 | func testEncryptDecrypt(t *testing.T, priv *PrivateKey, oid, fingerprint []byte) { 59 | message := []byte("hello world") 60 | 61 | vsG, m, err := Encrypt(rand.Reader, &priv.PublicKey, message, oid, fingerprint) 62 | if err != nil { 63 | t.Errorf("error encrypting: %s", err) 64 | } 65 | 66 | message2, err := Decrypt(priv, vsG, m, oid, fingerprint) 67 | if err != nil { 68 | t.Errorf("error decrypting: %s", err) 69 | } 70 | 71 | if !bytes.Equal(message2, message) { 72 | t.Errorf("decryption failed, got: %x, want: %x", message2, message) 73 | } 74 | } 75 | 76 | func testValidation(t *testing.T, priv *PrivateKey) { 77 | if err := Validate(priv); err != nil { 78 | t.Fatalf("valid key marked as invalid: %s", err) 79 | } 80 | 81 | priv.D[5] ^= 1 82 | if err := Validate(priv); err == nil { 83 | t.Fatalf("failed to detect invalid key") 84 | } 85 | } 86 | 87 | func testMarshalUnmarshal(t *testing.T, priv *PrivateKey) { 88 | p := priv.MarshalPoint() 89 | d := priv.MarshalByteSecret() 90 | 91 | parsed := NewPrivateKey(*NewPublicKey(priv.GetCurve(), priv.KDF.Hash, priv.KDF.Cipher)) 92 | 93 | if err := parsed.UnmarshalPoint(p); err != nil { 94 | t.Fatalf("unable to unmarshal point: %s", err) 95 | } 96 | 97 | if err := parsed.UnmarshalByteSecret(d); err != nil { 98 | t.Fatalf("unable to unmarshal integer: %s", err) 99 | } 100 | 101 | expectedD := make([]byte, len(priv.D)) 102 | copy(expectedD, priv.D) 103 | 104 | // Curve25519 expects keys to be saved clamped 105 | if priv.curve.GetCurveName() == "curve25519" { 106 | expectedD[0] &= 248 107 | expectedD[31] &= 127 108 | expectedD[31] |= 64 109 | } 110 | 111 | if !bytes.Equal(priv.Point, parsed.Point) || !bytes.Equal(expectedD, parsed.D) { 112 | t.Fatal("failed to marshal/unmarshal correctly") 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /openpgp/packet/symmetrically_encrypted.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package packet 6 | 7 | import ( 8 | "io" 9 | 10 | "github.com/ProtonMail/go-crypto/openpgp/errors" 11 | ) 12 | 13 | const aeadSaltSize = 32 14 | 15 | // SymmetricallyEncrypted represents a symmetrically encrypted byte string. The 16 | // encrypted Contents will consist of more OpenPGP packets. See RFC 4880, 17 | // sections 5.7 and 5.13. 18 | type SymmetricallyEncrypted struct { 19 | Version int 20 | Contents io.Reader // contains tag for version 2 21 | IntegrityProtected bool // If true it is type 18 (with MDC or AEAD). False is packet type 9 22 | 23 | // Specific to version 1 24 | prefix []byte 25 | 26 | // Specific to version 2 27 | Cipher CipherFunction 28 | Mode AEADMode 29 | ChunkSizeByte byte 30 | Salt [aeadSaltSize]byte 31 | } 32 | 33 | const ( 34 | symmetricallyEncryptedVersionMdc = 1 35 | symmetricallyEncryptedVersionAead = 2 36 | ) 37 | 38 | func (se *SymmetricallyEncrypted) parse(r io.Reader) error { 39 | if se.IntegrityProtected { 40 | // See RFC 4880, section 5.13. 41 | var buf [1]byte 42 | _, err := readFull(r, buf[:]) 43 | if err != nil { 44 | return err 45 | } 46 | 47 | switch buf[0] { 48 | case symmetricallyEncryptedVersionMdc: 49 | se.Version = symmetricallyEncryptedVersionMdc 50 | case symmetricallyEncryptedVersionAead: 51 | se.Version = symmetricallyEncryptedVersionAead 52 | if err := se.parseAead(r); err != nil { 53 | return err 54 | } 55 | default: 56 | return errors.UnsupportedError("unknown SymmetricallyEncrypted version") 57 | } 58 | } 59 | se.Contents = r 60 | return nil 61 | } 62 | 63 | // Decrypt returns a ReadCloser, from which the decrypted Contents of the 64 | // packet can be read. An incorrect key will only be detected after trying 65 | // to decrypt the entire data. 66 | func (se *SymmetricallyEncrypted) Decrypt(c CipherFunction, key []byte) (io.ReadCloser, error) { 67 | if se.Version == symmetricallyEncryptedVersionAead { 68 | return se.decryptAead(key) 69 | } 70 | 71 | return se.decryptMdc(c, key) 72 | } 73 | 74 | // SerializeSymmetricallyEncrypted serializes a symmetrically encrypted packet 75 | // to w and returns a WriteCloser to which the to-be-encrypted packets can be 76 | // written. 77 | // If aeadSupported is set to true, SEIPDv2 is used with the indicated CipherSuite. 78 | // Otherwise, SEIPDv1 is used with the indicated CipherFunction. 79 | // Note: aeadSupported MUST match the value passed to SerializeEncryptedKeyAEAD 80 | // and/or SerializeSymmetricKeyEncryptedAEADReuseKey. 81 | // If config is nil, sensible defaults will be used. 82 | func SerializeSymmetricallyEncrypted(w io.Writer, c CipherFunction, aeadSupported bool, cipherSuite CipherSuite, key []byte, config *Config) (Contents io.WriteCloser, err error) { 83 | writeCloser := noOpCloser{w} 84 | ciphertext, err := serializeStreamHeader(writeCloser, packetTypeSymmetricallyEncryptedIntegrityProtected) 85 | if err != nil { 86 | return 87 | } 88 | 89 | if aeadSupported { 90 | return serializeSymmetricallyEncryptedAead(ciphertext, cipherSuite, config.AEADConfig.ChunkSizeByte(), config.Random(), key) 91 | } 92 | 93 | return serializeSymmetricallyEncryptedMdc(ciphertext, c, key, config) 94 | } 95 | -------------------------------------------------------------------------------- /openpgp/packet/aead_encrypted.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 ProtonTech AG 2 | 3 | package packet 4 | 5 | import ( 6 | "io" 7 | 8 | "github.com/ProtonMail/go-crypto/openpgp/errors" 9 | "github.com/ProtonMail/go-crypto/openpgp/internal/algorithm" 10 | ) 11 | 12 | // AEADEncrypted represents an AEAD Encrypted Packet. 13 | // See https://www.ietf.org/archive/id/draft-koch-openpgp-2015-rfc4880bis-00.html#name-aead-encrypted-data-packet-t 14 | type AEADEncrypted struct { 15 | cipher CipherFunction 16 | mode AEADMode 17 | chunkSizeByte byte 18 | Contents io.Reader // Encrypted chunks and tags 19 | initialNonce []byte // Referred to as IV in RFC4880-bis 20 | } 21 | 22 | // Only currently defined version 23 | const aeadEncryptedVersion = 1 24 | 25 | func (ae *AEADEncrypted) parse(buf io.Reader) error { 26 | headerData := make([]byte, 4) 27 | if n, err := io.ReadFull(buf, headerData); n < 4 { 28 | return errors.AEADError("could not read aead header:" + err.Error()) 29 | } 30 | // Read initial nonce 31 | mode := AEADMode(headerData[2]) 32 | nonceLen := mode.IvLength() 33 | 34 | // This packet supports only EAX and OCB 35 | // https://www.ietf.org/archive/id/draft-koch-openpgp-2015-rfc4880bis-00.html#name-aead-encrypted-data-packet-t 36 | if nonceLen == 0 || mode > AEADModeOCB { 37 | return errors.AEADError("unknown mode") 38 | } 39 | 40 | initialNonce := make([]byte, nonceLen) 41 | if n, err := io.ReadFull(buf, initialNonce); n < nonceLen { 42 | return errors.AEADError("could not read aead nonce:" + err.Error()) 43 | } 44 | ae.Contents = buf 45 | ae.initialNonce = initialNonce 46 | c := headerData[1] 47 | if _, ok := algorithm.CipherById[c]; !ok { 48 | return errors.UnsupportedError("unknown cipher: " + string(c)) 49 | } 50 | ae.cipher = CipherFunction(c) 51 | ae.mode = mode 52 | ae.chunkSizeByte = headerData[3] 53 | return nil 54 | } 55 | 56 | // Decrypt returns a io.ReadCloser from which decrypted bytes can be read, or 57 | // an error. 58 | func (ae *AEADEncrypted) Decrypt(ciph CipherFunction, key []byte) (io.ReadCloser, error) { 59 | return ae.decrypt(key) 60 | } 61 | 62 | // decrypt prepares an aeadCrypter and returns a ReadCloser from which 63 | // decrypted bytes can be read (see aeadDecrypter.Read()). 64 | func (ae *AEADEncrypted) decrypt(key []byte) (io.ReadCloser, error) { 65 | blockCipher := ae.cipher.new(key) 66 | aead := ae.mode.new(blockCipher) 67 | // Carry the first tagLen bytes 68 | chunkSize := decodeAEADChunkSize(ae.chunkSizeByte) 69 | tagLen := ae.mode.TagLength() 70 | chunkBytes := make([]byte, chunkSize+tagLen*2) 71 | peekedBytes := chunkBytes[chunkSize+tagLen:] 72 | n, err := io.ReadFull(ae.Contents, peekedBytes) 73 | if n < tagLen || (err != nil && err != io.EOF) { 74 | return nil, errors.AEADError("Not enough data to decrypt:" + err.Error()) 75 | } 76 | 77 | return &aeadDecrypter{ 78 | aeadCrypter: aeadCrypter{ 79 | aead: aead, 80 | chunkSize: chunkSize, 81 | nonce: ae.initialNonce, 82 | associatedData: ae.associatedData(), 83 | chunkIndex: make([]byte, 8), 84 | packetTag: packetTypeAEADEncrypted, 85 | }, 86 | reader: ae.Contents, 87 | chunkBytes: chunkBytes, 88 | peekedBytes: peekedBytes, 89 | }, nil 90 | } 91 | 92 | // associatedData for chunks: tag, version, cipher, mode, chunk size byte 93 | func (ae *AEADEncrypted) associatedData() []byte { 94 | return []byte{ 95 | 0xD4, 96 | aeadEncryptedVersion, 97 | byte(ae.cipher), 98 | byte(ae.mode), 99 | ae.chunkSizeByte} 100 | } 101 | -------------------------------------------------------------------------------- /openpgp/packet/public_key_test_data.go: -------------------------------------------------------------------------------- 1 | package packet 2 | 3 | const rsaFingerprintHex = "5fb74b1d03b1e3cb31bc2f8aa34d7e18c20c31bb" 4 | 5 | const rsaPkDataHex = "988d044d3c5c10010400b1d13382944bd5aba23a4312968b5095d14f947f600eb478e14a6fcb16b0e0cac764884909c020bc495cfcc39a935387c661507bdb236a0612fb582cac3af9b29cc2c8c70090616c41b662f4da4c1201e195472eb7f4ae1ccbcbf9940fe21d985e379a5563dde5b9a23d35f1cfaa5790da3b79db26f23695107bfaca8e7b5bcd0011010001" 6 | 7 | const dsaFingerprintHex = "eece4c094db002103714c63c8e8fbe54062f19ed" 8 | 9 | const dsaPkDataHex = "9901a2044d432f89110400cd581334f0d7a1e1bdc8b9d6d8c0baf68793632735d2bb0903224cbaa1dfbf35a60ee7a13b92643421e1eb41aa8d79bea19a115a677f6b8ba3c7818ce53a6c2a24a1608bd8b8d6e55c5090cbde09dd26e356267465ae25e69ec8bdd57c7bbb2623e4d73336f73a0a9098f7f16da2e25252130fd694c0e8070c55a812a423ae7f00a0ebf50e70c2f19c3520a551bd4b08d30f23530d3d03ff7d0bf4a53a64a09dc5e6e6e35854b7d70c882b0c60293401958b1bd9e40abec3ea05ba87cf64899299d4bd6aa7f459c201d3fbbd6c82004bdc5e8a9eb8082d12054cc90fa9d4ec251a843236a588bf49552441817436c4f43326966fe85447d4e6d0acf8fa1ef0f014730770603ad7634c3088dc52501c237328417c31c89ed70400b2f1a98b0bf42f11fefc430704bebbaa41d9f355600c3facee1e490f64208e0e094ea55e3a598a219a58500bf78ac677b670a14f4e47e9cf8eab4f368cc1ddcaa18cc59309d4cc62dd4f680e73e6cc3e1ce87a84d0925efbcb26c575c093fc42eecf45135fabf6403a25c2016e1774c0484e440a18319072c617cc97ac0a3bb0" 10 | 11 | const ecdsaFingerprintHex = "9892270b38b8980b05c8d56d43fe956c542ca00b" 12 | 13 | const ecdsaPkDataHex = "9893045071c29413052b8104002304230401f4867769cedfa52c325018896245443968e52e51d0c2df8d939949cb5b330f2921711fbee1c9b9dddb95d15cb0255e99badeddda7cc23d9ddcaacbc290969b9f24019375d61c2e4e3b36953a28d8b2bc95f78c3f1d592fb24499be348656a7b17e3963187b4361afe497bc5f9f81213f04069f8e1fb9e6a6290ae295ca1a92b894396cb4" 14 | 15 | const ecdhFingerprintHex = "722354df2475a42164d1d49faa8b938f9a201946" 16 | 17 | const ecdhPkDataHex = "b90073044d53059212052b810400220303042faa84024a20b6735c4897efa5bfb41bf85b7eefeab5ca0cb9ffc8ea04a46acb25534a577694f9e25340a4ab5223a9dd1eda530c8aa2e6718db10d7e672558c7736fe09369ea5739a2a3554bf16d41faa50562f11c6d39bbd5dffb6b9a9ec91803010909" 18 | 19 | const eddsaFingerprintHex = "b2d5e5ec0e6deca6bc8eeeb00907e75e1dd99ad8" 20 | 21 | const eddsaPkDataHex = "98330456e2132b16092b06010401da470f01010740bbda39266affa511a8c2d02edf690fb784b0499c4406185811a163539ef11dc1b41d74657374696e67203c74657374696e674074657374696e672e636f6d3e8879041316080021050256e2132b021b03050b09080702061508090a0b020416020301021e01021780000a09100907e75e1dd99ad86d0c00fe39d2008359352782bc9b61ac382584cd8eff3f57a18c2287e3afeeb05d1f04ba00fe2d0bc1ddf3ff8adb9afa3e7d9287244b4ec567f3db4d60b74a9b5465ed528203" 22 | 23 | // Source: https://sites.google.com/site/brainhub/pgpecckeys#TOC-ECC-NIST-P-384-key 24 | const ecc384PubHex = `99006f044d53059213052b81040022030304f6b8c5aced5b84ef9f4a209db2e4a9dfb70d28cb8c10ecd57674a9fa5a67389942b62d5e51367df4c7bfd3f8e500feecf07ed265a621a8ebbbe53e947ec78c677eba143bd1533c2b350e1c29f82313e1e1108eba063be1e64b10e6950e799c2db42465635f6473615f64685f333834203c6f70656e70677040627261696e6875622e6f72673e8900cb04101309005305024d530592301480000000002000077072656665727265642d656d61696c2d656e636f64696e67407067702e636f6d7067706d696d65040b090807021901051b03000000021602051e010000000415090a08000a0910098033880f54719fca2b0180aa37350968bd5f115afd8ce7bc7b103822152dbff06d0afcda835329510905b98cb469ba208faab87c7412b799e7b633017f58364ea480e8a1a3f253a0c5f22c446e8be9a9fce6210136ee30811abbd49139de28b5bdf8dc36d06ae748579e9ff503b90073044d53059212052b810400220303042faa84024a20b6735c4897efa5bfb41bf85b7eefeab5ca0cb9ffc8ea04a46acb25534a577694f9e25340a4ab5223a9dd1eda530c8aa2e6718db10d7e672558c7736fe09369ea5739a2a3554bf16d41faa50562f11c6d39bbd5dffb6b9a9ec9180301090989008404181309000c05024d530592051b0c000000000a0910098033880f54719f80970180eee7a6d8fcee41ee4f9289df17f9bcf9d955dca25c583b94336f3a2b2d4986dc5cf417b8d2dc86f741a9e1a6d236c0e3017d1c76575458a0cfb93ae8a2b274fcc65ceecd7a91eec83656ba13219969f06945b48c56bd04152c3a0553c5f2f4bd1267` 25 | -------------------------------------------------------------------------------- /openpgp/internal/ecc/curve_info.go: -------------------------------------------------------------------------------- 1 | // Package ecc implements a generic interface for ECDH, ECDSA, and EdDSA. 2 | package ecc 3 | 4 | import ( 5 | "bytes" 6 | "crypto/elliptic" 7 | 8 | "github.com/ProtonMail/go-crypto/bitcurves" 9 | "github.com/ProtonMail/go-crypto/brainpool" 10 | "github.com/ProtonMail/go-crypto/openpgp/internal/encoding" 11 | ) 12 | 13 | const Curve25519GenName = "Curve25519" 14 | 15 | type CurveInfo struct { 16 | GenName string 17 | Oid *encoding.OID 18 | Curve Curve 19 | } 20 | 21 | var Curves = []CurveInfo{ 22 | { 23 | // NIST P-256 24 | GenName: "P256", 25 | Oid: encoding.NewOID([]byte{0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07}), 26 | Curve: NewGenericCurve(elliptic.P256()), 27 | }, 28 | { 29 | // NIST P-384 30 | GenName: "P384", 31 | Oid: encoding.NewOID([]byte{0x2B, 0x81, 0x04, 0x00, 0x22}), 32 | Curve: NewGenericCurve(elliptic.P384()), 33 | }, 34 | { 35 | // NIST P-521 36 | GenName: "P521", 37 | Oid: encoding.NewOID([]byte{0x2B, 0x81, 0x04, 0x00, 0x23}), 38 | Curve: NewGenericCurve(elliptic.P521()), 39 | }, 40 | { 41 | // SecP256k1 42 | GenName: "SecP256k1", 43 | Oid: encoding.NewOID([]byte{0x2B, 0x81, 0x04, 0x00, 0x0A}), 44 | Curve: NewGenericCurve(bitcurves.S256()), 45 | }, 46 | { 47 | // Curve25519 48 | GenName: Curve25519GenName, 49 | Oid: encoding.NewOID([]byte{0x2B, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01}), 50 | Curve: NewCurve25519(), 51 | }, 52 | { 53 | // x448 54 | GenName: "Curve448", 55 | Oid: encoding.NewOID([]byte{0x2B, 0x65, 0x6F}), 56 | Curve: NewX448(), 57 | }, 58 | { 59 | // Ed25519 60 | GenName: Curve25519GenName, 61 | Oid: encoding.NewOID([]byte{0x2B, 0x06, 0x01, 0x04, 0x01, 0xDA, 0x47, 0x0F, 0x01}), 62 | Curve: NewEd25519(), 63 | }, 64 | { 65 | // Ed448 66 | GenName: "Curve448", 67 | Oid: encoding.NewOID([]byte{0x2B, 0x65, 0x71}), 68 | Curve: NewEd448(), 69 | }, 70 | { 71 | // BrainpoolP256r1 72 | GenName: "BrainpoolP256", 73 | Oid: encoding.NewOID([]byte{0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x07}), 74 | Curve: NewGenericCurve(brainpool.P256r1()), 75 | }, 76 | { 77 | // BrainpoolP384r1 78 | GenName: "BrainpoolP384", 79 | Oid: encoding.NewOID([]byte{0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0B}), 80 | Curve: NewGenericCurve(brainpool.P384r1()), 81 | }, 82 | { 83 | // BrainpoolP512r1 84 | GenName: "BrainpoolP512", 85 | Oid: encoding.NewOID([]byte{0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0D}), 86 | Curve: NewGenericCurve(brainpool.P512r1()), 87 | }, 88 | } 89 | 90 | func FindByCurve(curve Curve) *CurveInfo { 91 | for _, curveInfo := range Curves { 92 | if curveInfo.Curve.GetCurveName() == curve.GetCurveName() { 93 | return &curveInfo 94 | } 95 | } 96 | return nil 97 | } 98 | 99 | func FindByOid(oid encoding.Field) *CurveInfo { 100 | var rawBytes = oid.Bytes() 101 | for _, curveInfo := range Curves { 102 | if bytes.Equal(curveInfo.Oid.Bytes(), rawBytes) { 103 | return &curveInfo 104 | } 105 | } 106 | return nil 107 | } 108 | 109 | func FindEdDSAByGenName(curveGenName string) EdDSACurve { 110 | for _, curveInfo := range Curves { 111 | if curveInfo.GenName == curveGenName { 112 | curve, ok := curveInfo.Curve.(EdDSACurve) 113 | if ok { 114 | return curve 115 | } 116 | } 117 | } 118 | return nil 119 | } 120 | 121 | func FindECDSAByGenName(curveGenName string) ECDSACurve { 122 | for _, curveInfo := range Curves { 123 | if curveInfo.GenName == curveGenName { 124 | curve, ok := curveInfo.Curve.(ECDSACurve) 125 | if ok { 126 | return curve 127 | } 128 | } 129 | } 130 | return nil 131 | } 132 | 133 | func FindECDHByGenName(curveGenName string) ECDHCurve { 134 | for _, curveInfo := range Curves { 135 | if curveInfo.GenName == curveGenName { 136 | curve, ok := curveInfo.Curve.(ECDHCurve) 137 | if ok { 138 | return curve 139 | } 140 | } 141 | } 142 | return nil 143 | } 144 | -------------------------------------------------------------------------------- /openpgp/ed25519/ed25519.go: -------------------------------------------------------------------------------- 1 | // Package ed25519 implements the ed25519 signature algorithm for OpenPGP 2 | // as defined in the Open PGP crypto refresh. 3 | package ed25519 4 | 5 | import ( 6 | "crypto/subtle" 7 | "io" 8 | 9 | "github.com/ProtonMail/go-crypto/openpgp/errors" 10 | ed25519lib "github.com/cloudflare/circl/sign/ed25519" 11 | ) 12 | 13 | const ( 14 | // PublicKeySize is the size, in bytes, of public keys in this package. 15 | PublicKeySize = ed25519lib.PublicKeySize 16 | // SeedSize is the size, in bytes, of private key seeds. 17 | // The private key representation used by RFC 8032. 18 | SeedSize = ed25519lib.SeedSize 19 | // SignatureSize is the size, in bytes, of signatures generated and verified by this package. 20 | SignatureSize = ed25519lib.SignatureSize 21 | ) 22 | 23 | type PublicKey struct { 24 | // Point represents the elliptic curve point of the public key. 25 | Point []byte 26 | } 27 | 28 | type PrivateKey struct { 29 | PublicKey 30 | // Key the private key representation by RFC 8032, 31 | // encoded as seed | pub key point. 32 | Key []byte 33 | } 34 | 35 | // NewPublicKey creates a new empty ed25519 public key. 36 | func NewPublicKey() *PublicKey { 37 | return &PublicKey{} 38 | } 39 | 40 | // NewPrivateKey creates a new empty private key referencing the public key. 41 | func NewPrivateKey(key PublicKey) *PrivateKey { 42 | return &PrivateKey{ 43 | PublicKey: key, 44 | } 45 | } 46 | 47 | // Seed returns the ed25519 private key secret seed. 48 | // The private key representation by RFC 8032. 49 | func (pk *PrivateKey) Seed() []byte { 50 | return pk.Key[:SeedSize] 51 | } 52 | 53 | // MarshalByteSecret returns the underlying 32 byte seed of the private key. 54 | func (pk *PrivateKey) MarshalByteSecret() []byte { 55 | return pk.Seed() 56 | } 57 | 58 | // UnmarshalByteSecret computes the private key from the secret seed 59 | // and stores it in the private key object. 60 | func (sk *PrivateKey) UnmarshalByteSecret(seed []byte) error { 61 | sk.Key = ed25519lib.NewKeyFromSeed(seed) 62 | return nil 63 | } 64 | 65 | // GenerateKey generates a fresh private key with the provided randomness source. 66 | func GenerateKey(rand io.Reader) (*PrivateKey, error) { 67 | publicKey, privateKey, err := ed25519lib.GenerateKey(rand) 68 | if err != nil { 69 | return nil, err 70 | } 71 | privateKeyOut := new(PrivateKey) 72 | privateKeyOut.PublicKey.Point = publicKey[:] 73 | privateKeyOut.Key = privateKey[:] 74 | return privateKeyOut, nil 75 | } 76 | 77 | // Sign signs a message with the ed25519 algorithm. 78 | // priv MUST be a valid key! Check this with Validate() before use. 79 | func Sign(priv *PrivateKey, message []byte) ([]byte, error) { 80 | return ed25519lib.Sign(priv.Key, message), nil 81 | } 82 | 83 | // Verify verifies an ed25519 signature. 84 | func Verify(pub *PublicKey, message []byte, signature []byte) bool { 85 | return ed25519lib.Verify(pub.Point, message, signature) 86 | } 87 | 88 | // Validate checks if the ed25519 private key is valid. 89 | func Validate(priv *PrivateKey) error { 90 | expectedPrivateKey := ed25519lib.NewKeyFromSeed(priv.Seed()) 91 | if subtle.ConstantTimeCompare(priv.Key, expectedPrivateKey) == 0 { 92 | return errors.KeyInvalidError("ed25519: invalid ed25519 secret") 93 | } 94 | if subtle.ConstantTimeCompare(priv.PublicKey.Point, expectedPrivateKey[SeedSize:]) == 0 { 95 | return errors.KeyInvalidError("ed25519: invalid ed25519 public key") 96 | } 97 | return nil 98 | } 99 | 100 | // ENCODING/DECODING signature: 101 | 102 | // WriteSignature encodes and writes an ed25519 signature to writer. 103 | func WriteSignature(writer io.Writer, signature []byte) error { 104 | _, err := writer.Write(signature) 105 | return err 106 | } 107 | 108 | // ReadSignature decodes an ed25519 signature from a reader. 109 | func ReadSignature(reader io.Reader) ([]byte, error) { 110 | signature := make([]byte, SignatureSize) 111 | if _, err := io.ReadFull(reader, signature); err != nil { 112 | return nil, err 113 | } 114 | return signature, nil 115 | } 116 | -------------------------------------------------------------------------------- /openpgp/packet/ocfb.go: -------------------------------------------------------------------------------- 1 | // Copyright 2010 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // OpenPGP CFB Mode. http://tools.ietf.org/html/rfc4880#section-13.9 6 | 7 | package packet 8 | 9 | import ( 10 | "crypto/cipher" 11 | ) 12 | 13 | type ocfbEncrypter struct { 14 | b cipher.Block 15 | fre []byte 16 | outUsed int 17 | } 18 | 19 | // An OCFBResyncOption determines if the "resynchronization step" of OCFB is 20 | // performed. 21 | type OCFBResyncOption bool 22 | 23 | const ( 24 | OCFBResync OCFBResyncOption = true 25 | OCFBNoResync OCFBResyncOption = false 26 | ) 27 | 28 | // NewOCFBEncrypter returns a cipher.Stream which encrypts data with OpenPGP's 29 | // cipher feedback mode using the given cipher.Block, and an initial amount of 30 | // ciphertext. randData must be random bytes and be the same length as the 31 | // cipher.Block's block size. Resync determines if the "resynchronization step" 32 | // from RFC 4880, 13.9 step 7 is performed. Different parts of OpenPGP vary on 33 | // this point. 34 | func NewOCFBEncrypter(block cipher.Block, randData []byte, resync OCFBResyncOption) (cipher.Stream, []byte) { 35 | blockSize := block.BlockSize() 36 | if len(randData) != blockSize { 37 | return nil, nil 38 | } 39 | 40 | x := &ocfbEncrypter{ 41 | b: block, 42 | fre: make([]byte, blockSize), 43 | outUsed: 0, 44 | } 45 | prefix := make([]byte, blockSize+2) 46 | 47 | block.Encrypt(x.fre, x.fre) 48 | for i := 0; i < blockSize; i++ { 49 | prefix[i] = randData[i] ^ x.fre[i] 50 | } 51 | 52 | block.Encrypt(x.fre, prefix[:blockSize]) 53 | prefix[blockSize] = x.fre[0] ^ randData[blockSize-2] 54 | prefix[blockSize+1] = x.fre[1] ^ randData[blockSize-1] 55 | 56 | if resync { 57 | block.Encrypt(x.fre, prefix[2:]) 58 | } else { 59 | x.fre[0] = prefix[blockSize] 60 | x.fre[1] = prefix[blockSize+1] 61 | x.outUsed = 2 62 | } 63 | return x, prefix 64 | } 65 | 66 | func (x *ocfbEncrypter) XORKeyStream(dst, src []byte) { 67 | for i := 0; i < len(src); i++ { 68 | if x.outUsed == len(x.fre) { 69 | x.b.Encrypt(x.fre, x.fre) 70 | x.outUsed = 0 71 | } 72 | 73 | x.fre[x.outUsed] ^= src[i] 74 | dst[i] = x.fre[x.outUsed] 75 | x.outUsed++ 76 | } 77 | } 78 | 79 | type ocfbDecrypter struct { 80 | b cipher.Block 81 | fre []byte 82 | outUsed int 83 | } 84 | 85 | // NewOCFBDecrypter returns a cipher.Stream which decrypts data with OpenPGP's 86 | // cipher feedback mode using the given cipher.Block. Prefix must be the first 87 | // blockSize + 2 bytes of the ciphertext, where blockSize is the cipher.Block's 88 | // block size. On successful exit, blockSize+2 bytes of decrypted data are written into 89 | // prefix. Resync determines if the "resynchronization step" from RFC 4880, 90 | // 13.9 step 7 is performed. Different parts of OpenPGP vary on this point. 91 | func NewOCFBDecrypter(block cipher.Block, prefix []byte, resync OCFBResyncOption) cipher.Stream { 92 | blockSize := block.BlockSize() 93 | if len(prefix) != blockSize+2 { 94 | return nil 95 | } 96 | 97 | x := &ocfbDecrypter{ 98 | b: block, 99 | fre: make([]byte, blockSize), 100 | outUsed: 0, 101 | } 102 | prefixCopy := make([]byte, len(prefix)) 103 | copy(prefixCopy, prefix) 104 | 105 | block.Encrypt(x.fre, x.fre) 106 | for i := 0; i < blockSize; i++ { 107 | prefixCopy[i] ^= x.fre[i] 108 | } 109 | 110 | block.Encrypt(x.fre, prefix[:blockSize]) 111 | prefixCopy[blockSize] ^= x.fre[0] 112 | prefixCopy[blockSize+1] ^= x.fre[1] 113 | 114 | if resync { 115 | block.Encrypt(x.fre, prefix[2:]) 116 | } else { 117 | x.fre[0] = prefix[blockSize] 118 | x.fre[1] = prefix[blockSize+1] 119 | x.outUsed = 2 120 | } 121 | copy(prefix, prefixCopy) 122 | return x 123 | } 124 | 125 | func (x *ocfbDecrypter) XORKeyStream(dst, src []byte) { 126 | for i := 0; i < len(src); i++ { 127 | if x.outUsed == len(x.fre) { 128 | x.b.Encrypt(x.fre, x.fre) 129 | x.outUsed = 0 130 | } 131 | 132 | c := src[i] 133 | dst[i] = x.fre[x.outUsed] ^ src[i] 134 | x.fre[x.outUsed] = c 135 | x.outUsed++ 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /openpgp/elgamal/elgamal.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package elgamal implements ElGamal encryption, suitable for OpenPGP, 6 | // as specified in "A Public-Key Cryptosystem and a Signature Scheme Based on 7 | // Discrete Logarithms," IEEE Transactions on Information Theory, v. IT-31, 8 | // n. 4, 1985, pp. 469-472. 9 | // 10 | // This form of ElGamal embeds PKCS#1 v1.5 padding, which may make it 11 | // unsuitable for other protocols. RSA should be used in preference in any 12 | // case. 13 | package elgamal // import "github.com/ProtonMail/go-crypto/openpgp/elgamal" 14 | 15 | import ( 16 | "crypto/rand" 17 | "crypto/subtle" 18 | "errors" 19 | "io" 20 | "math/big" 21 | ) 22 | 23 | // PublicKey represents an ElGamal public key. 24 | type PublicKey struct { 25 | G, P, Y *big.Int 26 | } 27 | 28 | // PrivateKey represents an ElGamal private key. 29 | type PrivateKey struct { 30 | PublicKey 31 | X *big.Int 32 | } 33 | 34 | // Encrypt encrypts the given message to the given public key. The result is a 35 | // pair of integers. Errors can result from reading random, or because msg is 36 | // too large to be encrypted to the public key. 37 | func Encrypt(random io.Reader, pub *PublicKey, msg []byte) (c1, c2 *big.Int, err error) { 38 | pLen := (pub.P.BitLen() + 7) / 8 39 | if len(msg) > pLen-11 { 40 | err = errors.New("elgamal: message too long") 41 | return 42 | } 43 | 44 | // EM = 0x02 || PS || 0x00 || M 45 | em := make([]byte, pLen-1) 46 | em[0] = 2 47 | ps, mm := em[1:len(em)-len(msg)-1], em[len(em)-len(msg):] 48 | err = nonZeroRandomBytes(ps, random) 49 | if err != nil { 50 | return 51 | } 52 | em[len(em)-len(msg)-1] = 0 53 | copy(mm, msg) 54 | 55 | m := new(big.Int).SetBytes(em) 56 | 57 | k, err := rand.Int(random, pub.P) 58 | if err != nil { 59 | return 60 | } 61 | 62 | c1 = new(big.Int).Exp(pub.G, k, pub.P) 63 | s := new(big.Int).Exp(pub.Y, k, pub.P) 64 | c2 = s.Mul(s, m) 65 | c2.Mod(c2, pub.P) 66 | 67 | return 68 | } 69 | 70 | // Decrypt takes two integers, resulting from an ElGamal encryption, and 71 | // returns the plaintext of the message. An error can result only if the 72 | // ciphertext is invalid. Users should keep in mind that this is a padding 73 | // oracle and thus, if exposed to an adaptive chosen ciphertext attack, can 74 | // be used to break the cryptosystem. See “Chosen Ciphertext Attacks 75 | // Against Protocols Based on the RSA Encryption Standard PKCS #1”, Daniel 76 | // Bleichenbacher, Advances in Cryptology (Crypto '98), 77 | func Decrypt(priv *PrivateKey, c1, c2 *big.Int) (msg []byte, err error) { 78 | s := new(big.Int).Exp(c1, priv.X, priv.P) 79 | if s.ModInverse(s, priv.P) == nil { 80 | return nil, errors.New("elgamal: invalid private key") 81 | } 82 | s.Mul(s, c2) 83 | s.Mod(s, priv.P) 84 | em := s.Bytes() 85 | 86 | firstByteIsTwo := subtle.ConstantTimeByteEq(em[0], 2) 87 | 88 | // The remainder of the plaintext must be a string of non-zero random 89 | // octets, followed by a 0, followed by the message. 90 | // lookingForIndex: 1 iff we are still looking for the zero. 91 | // index: the offset of the first zero byte. 92 | var lookingForIndex, index int 93 | lookingForIndex = 1 94 | 95 | for i := 1; i < len(em); i++ { 96 | equals0 := subtle.ConstantTimeByteEq(em[i], 0) 97 | index = subtle.ConstantTimeSelect(lookingForIndex&equals0, i, index) 98 | lookingForIndex = subtle.ConstantTimeSelect(equals0, 0, lookingForIndex) 99 | } 100 | 101 | if firstByteIsTwo != 1 || lookingForIndex != 0 || index < 9 { 102 | return nil, errors.New("elgamal: decryption error") 103 | } 104 | return em[index+1:], nil 105 | } 106 | 107 | // nonZeroRandomBytes fills the given slice with non-zero random octets. 108 | func nonZeroRandomBytes(s []byte, rand io.Reader) (err error) { 109 | _, err = io.ReadFull(rand, s) 110 | if err != nil { 111 | return 112 | } 113 | 114 | for i := 0; i < len(s); i++ { 115 | for s[i] == 0 { 116 | _, err = io.ReadFull(rand, s[i:i+1]) 117 | if err != nil { 118 | return 119 | } 120 | } 121 | } 122 | 123 | return 124 | } 125 | -------------------------------------------------------------------------------- /openpgp/internal/ecc/ed25519.go: -------------------------------------------------------------------------------- 1 | // Package ecc implements a generic interface for ECDH, ECDSA, and EdDSA. 2 | package ecc 3 | 4 | import ( 5 | "bytes" 6 | "crypto/subtle" 7 | "io" 8 | 9 | "github.com/ProtonMail/go-crypto/openpgp/errors" 10 | ed25519lib "github.com/cloudflare/circl/sign/ed25519" 11 | ) 12 | 13 | const ed25519Size = 32 14 | 15 | type ed25519 struct{} 16 | 17 | func NewEd25519() *ed25519 { 18 | return &ed25519{} 19 | } 20 | 21 | func (c *ed25519) GetCurveName() string { 22 | return "ed25519" 23 | } 24 | 25 | // MarshalBytePoint encodes the public point from native format, adding the prefix. 26 | // See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-5.5.5.5 27 | func (c *ed25519) MarshalBytePoint(x []byte) []byte { 28 | return append([]byte{0x40}, x...) 29 | } 30 | 31 | // UnmarshalBytePoint decodes a point from prefixed format to native. 32 | // See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-5.5.5.5 33 | func (c *ed25519) UnmarshalBytePoint(point []byte) (x []byte) { 34 | if len(point) != ed25519lib.PublicKeySize+1 { 35 | return nil 36 | } 37 | 38 | // Return unprefixed 39 | return point[1:] 40 | } 41 | 42 | // MarshalByteSecret encodes a scalar in native format. 43 | // See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-5.5.5.5 44 | func (c *ed25519) MarshalByteSecret(d []byte) []byte { 45 | return d 46 | } 47 | 48 | // UnmarshalByteSecret decodes a scalar in native format and re-adds the stripped leading zeroes 49 | // See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-5.5.5.5 50 | func (c *ed25519) UnmarshalByteSecret(s []byte) (d []byte) { 51 | if len(s) > ed25519lib.SeedSize { 52 | return nil 53 | } 54 | 55 | // Handle stripped leading zeroes 56 | d = make([]byte, ed25519lib.SeedSize) 57 | copy(d[ed25519lib.SeedSize-len(s):], s) 58 | return 59 | } 60 | 61 | // MarshalSignature splits a signature in R and S. 62 | // See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-5.2.3.3.1 63 | func (c *ed25519) MarshalSignature(sig []byte) (r, s []byte) { 64 | return sig[:ed25519Size], sig[ed25519Size:] 65 | } 66 | 67 | // UnmarshalSignature decodes R and S in the native format, re-adding the stripped leading zeroes 68 | // See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-5.2.3.3.1 69 | func (c *ed25519) UnmarshalSignature(r, s []byte) (sig []byte) { 70 | // Check size 71 | if len(r) > 32 || len(s) > 32 { 72 | return nil 73 | } 74 | 75 | sig = make([]byte, ed25519lib.SignatureSize) 76 | 77 | // Handle stripped leading zeroes 78 | copy(sig[ed25519Size-len(r):ed25519Size], r) 79 | copy(sig[ed25519lib.SignatureSize-len(s):], s) 80 | return sig 81 | } 82 | 83 | func (c *ed25519) GenerateEdDSA(rand io.Reader) (pub, priv []byte, err error) { 84 | pk, sk, err := ed25519lib.GenerateKey(rand) 85 | 86 | if err != nil { 87 | return nil, nil, err 88 | } 89 | 90 | return pk, sk[:ed25519lib.SeedSize], nil 91 | } 92 | 93 | func getEd25519Sk(publicKey, privateKey []byte) ed25519lib.PrivateKey { 94 | privateKeyCap, privateKeyLen, publicKeyLen := cap(privateKey), len(privateKey), len(publicKey) 95 | 96 | if privateKeyCap >= privateKeyLen+publicKeyLen && 97 | bytes.Equal(privateKey[privateKeyLen:privateKeyLen+publicKeyLen], publicKey) { 98 | return privateKey[:privateKeyLen+publicKeyLen] 99 | } 100 | 101 | return append(privateKey[:privateKeyLen:privateKeyLen], publicKey...) 102 | } 103 | 104 | func (c *ed25519) Sign(publicKey, privateKey, message []byte) (sig []byte, err error) { 105 | sig = ed25519lib.Sign(getEd25519Sk(publicKey, privateKey), message) 106 | return sig, nil 107 | } 108 | 109 | func (c *ed25519) Verify(publicKey, message, sig []byte) bool { 110 | return ed25519lib.Verify(publicKey, message, sig) 111 | } 112 | 113 | func (c *ed25519) ValidateEdDSA(publicKey, privateKey []byte) (err error) { 114 | priv := getEd25519Sk(publicKey, privateKey) 115 | expectedPriv := ed25519lib.NewKeyFromSeed(priv.Seed()) 116 | if subtle.ConstantTimeCompare(priv, expectedPriv) == 0 { 117 | return errors.KeyInvalidError("ecc: invalid ed25519 secret") 118 | } 119 | return nil 120 | } 121 | -------------------------------------------------------------------------------- /openpgp/ed448/ed448.go: -------------------------------------------------------------------------------- 1 | // Package ed448 implements the ed448 signature algorithm for OpenPGP 2 | // as defined in the Open PGP crypto refresh. 3 | package ed448 4 | 5 | import ( 6 | "crypto/subtle" 7 | "io" 8 | 9 | "github.com/ProtonMail/go-crypto/openpgp/errors" 10 | ed448lib "github.com/cloudflare/circl/sign/ed448" 11 | ) 12 | 13 | const ( 14 | // PublicKeySize is the size, in bytes, of public keys in this package. 15 | PublicKeySize = ed448lib.PublicKeySize 16 | // SeedSize is the size, in bytes, of private key seeds. 17 | // The private key representation used by RFC 8032. 18 | SeedSize = ed448lib.SeedSize 19 | // SignatureSize is the size, in bytes, of signatures generated and verified by this package. 20 | SignatureSize = ed448lib.SignatureSize 21 | ) 22 | 23 | type PublicKey struct { 24 | // Point represents the elliptic curve point of the public key. 25 | Point []byte 26 | } 27 | 28 | type PrivateKey struct { 29 | PublicKey 30 | // Key the private key representation by RFC 8032, 31 | // encoded as seed | public key point. 32 | Key []byte 33 | } 34 | 35 | // NewPublicKey creates a new empty ed448 public key. 36 | func NewPublicKey() *PublicKey { 37 | return &PublicKey{} 38 | } 39 | 40 | // NewPrivateKey creates a new empty private key referencing the public key. 41 | func NewPrivateKey(key PublicKey) *PrivateKey { 42 | return &PrivateKey{ 43 | PublicKey: key, 44 | } 45 | } 46 | 47 | // Seed returns the ed448 private key secret seed. 48 | // The private key representation by RFC 8032. 49 | func (pk *PrivateKey) Seed() []byte { 50 | return pk.Key[:SeedSize] 51 | } 52 | 53 | // MarshalByteSecret returns the underlying seed of the private key. 54 | func (pk *PrivateKey) MarshalByteSecret() []byte { 55 | return pk.Seed() 56 | } 57 | 58 | // UnmarshalByteSecret computes the private key from the secret seed 59 | // and stores it in the private key object. 60 | func (sk *PrivateKey) UnmarshalByteSecret(seed []byte) error { 61 | sk.Key = ed448lib.NewKeyFromSeed(seed) 62 | return nil 63 | } 64 | 65 | // GenerateKey generates a fresh private key with the provided randomness source. 66 | func GenerateKey(rand io.Reader) (*PrivateKey, error) { 67 | publicKey, privateKey, err := ed448lib.GenerateKey(rand) 68 | if err != nil { 69 | return nil, err 70 | } 71 | privateKeyOut := new(PrivateKey) 72 | privateKeyOut.PublicKey.Point = publicKey[:] 73 | privateKeyOut.Key = privateKey[:] 74 | return privateKeyOut, nil 75 | } 76 | 77 | // Sign signs a message with the ed448 algorithm. 78 | // priv MUST be a valid key! Check this with Validate() before use. 79 | func Sign(priv *PrivateKey, message []byte) ([]byte, error) { 80 | // Ed448 is used with the empty string as a context string. 81 | // See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-08#section-13.7 82 | return ed448lib.Sign(priv.Key, message, ""), nil 83 | } 84 | 85 | // Verify verifies a ed448 signature 86 | func Verify(pub *PublicKey, message []byte, signature []byte) bool { 87 | // Ed448 is used with the empty string as a context string. 88 | // See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-08#section-13.7 89 | return ed448lib.Verify(pub.Point, message, signature, "") 90 | } 91 | 92 | // Validate checks if the ed448 private key is valid 93 | func Validate(priv *PrivateKey) error { 94 | expectedPrivateKey := ed448lib.NewKeyFromSeed(priv.Seed()) 95 | if subtle.ConstantTimeCompare(priv.Key, expectedPrivateKey) == 0 { 96 | return errors.KeyInvalidError("ed448: invalid ed448 secret") 97 | } 98 | if subtle.ConstantTimeCompare(priv.PublicKey.Point, expectedPrivateKey[SeedSize:]) == 0 { 99 | return errors.KeyInvalidError("ed448: invalid ed448 public key") 100 | } 101 | return nil 102 | } 103 | 104 | // ENCODING/DECODING signature: 105 | 106 | // WriteSignature encodes and writes an ed448 signature to writer. 107 | func WriteSignature(writer io.Writer, signature []byte) error { 108 | _, err := writer.Write(signature) 109 | return err 110 | } 111 | 112 | // ReadSignature decodes an ed448 signature from a reader. 113 | func ReadSignature(reader io.Reader) ([]byte, error) { 114 | signature := make([]byte, SignatureSize) 115 | if _, err := io.ReadFull(reader, signature); err != nil { 116 | return nil, err 117 | } 118 | return signature, nil 119 | } 120 | -------------------------------------------------------------------------------- /openpgp/internal/ecc/ed448.go: -------------------------------------------------------------------------------- 1 | // Package ecc implements a generic interface for ECDH, ECDSA, and EdDSA. 2 | package ecc 3 | 4 | import ( 5 | "bytes" 6 | "crypto/subtle" 7 | "io" 8 | 9 | "github.com/ProtonMail/go-crypto/openpgp/errors" 10 | ed448lib "github.com/cloudflare/circl/sign/ed448" 11 | ) 12 | 13 | type ed448 struct{} 14 | 15 | func NewEd448() *ed448 { 16 | return &ed448{} 17 | } 18 | 19 | func (c *ed448) GetCurveName() string { 20 | return "ed448" 21 | } 22 | 23 | // MarshalBytePoint encodes the public point from native format, adding the prefix. 24 | // See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-5.5.5.5 25 | func (c *ed448) MarshalBytePoint(x []byte) []byte { 26 | // Return prefixed 27 | return append([]byte{0x40}, x...) 28 | } 29 | 30 | // UnmarshalBytePoint decodes a point from prefixed format to native. 31 | // See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-5.5.5.5 32 | func (c *ed448) UnmarshalBytePoint(point []byte) (x []byte) { 33 | if len(point) != ed448lib.PublicKeySize+1 { 34 | return nil 35 | } 36 | 37 | // Strip prefix 38 | return point[1:] 39 | } 40 | 41 | // MarshalByteSecret encoded a scalar from native format to prefixed. 42 | // See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-5.5.5.5 43 | func (c *ed448) MarshalByteSecret(d []byte) []byte { 44 | // Return prefixed 45 | return append([]byte{0x40}, d...) 46 | } 47 | 48 | // UnmarshalByteSecret decodes a scalar from prefixed format to native. 49 | // See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-5.5.5.5 50 | func (c *ed448) UnmarshalByteSecret(s []byte) (d []byte) { 51 | // Check prefixed size 52 | if len(s) != ed448lib.SeedSize+1 { 53 | return nil 54 | } 55 | 56 | // Strip prefix 57 | return s[1:] 58 | } 59 | 60 | // MarshalSignature splits a signature in R and S, where R is in prefixed native format and 61 | // S is an MPI with value zero. 62 | // See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-5.2.3.3.2 63 | func (c *ed448) MarshalSignature(sig []byte) (r, s []byte) { 64 | return append([]byte{0x40}, sig...), []byte{} 65 | } 66 | 67 | // UnmarshalSignature decodes R and S in the native format. Only R is used, in prefixed native format. 68 | // See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-5.2.3.3.2 69 | func (c *ed448) UnmarshalSignature(r, s []byte) (sig []byte) { 70 | if len(r) != ed448lib.SignatureSize+1 { 71 | return nil 72 | } 73 | 74 | return r[1:] 75 | } 76 | 77 | func (c *ed448) GenerateEdDSA(rand io.Reader) (pub, priv []byte, err error) { 78 | pk, sk, err := ed448lib.GenerateKey(rand) 79 | 80 | if err != nil { 81 | return nil, nil, err 82 | } 83 | 84 | return pk, sk[:ed448lib.SeedSize], nil 85 | } 86 | 87 | func getEd448Sk(publicKey, privateKey []byte) ed448lib.PrivateKey { 88 | privateKeyCap, privateKeyLen, publicKeyLen := cap(privateKey), len(privateKey), len(publicKey) 89 | 90 | if privateKeyCap >= privateKeyLen+publicKeyLen && 91 | bytes.Equal(privateKey[privateKeyLen:privateKeyLen+publicKeyLen], publicKey) { 92 | return privateKey[:privateKeyLen+publicKeyLen] 93 | } 94 | 95 | return append(privateKey[:privateKeyLen:privateKeyLen], publicKey...) 96 | } 97 | 98 | func (c *ed448) Sign(publicKey, privateKey, message []byte) (sig []byte, err error) { 99 | // Ed448 is used with the empty string as a context string. 100 | // See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-13.7 101 | sig = ed448lib.Sign(getEd448Sk(publicKey, privateKey), message, "") 102 | 103 | return sig, nil 104 | } 105 | 106 | func (c *ed448) Verify(publicKey, message, sig []byte) bool { 107 | // Ed448 is used with the empty string as a context string. 108 | // See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-crypto-refresh-06#section-13.7 109 | return ed448lib.Verify(publicKey, message, sig, "") 110 | } 111 | 112 | func (c *ed448) ValidateEdDSA(publicKey, privateKey []byte) (err error) { 113 | priv := getEd448Sk(publicKey, privateKey) 114 | expectedPriv := ed448lib.NewKeyFromSeed(priv.Seed()) 115 | if subtle.ConstantTimeCompare(priv, expectedPriv) == 0 { 116 | return errors.KeyInvalidError("ecc: invalid ed448 secret") 117 | } 118 | return nil 119 | } 120 | -------------------------------------------------------------------------------- /openpgp/packet/one_pass_signature.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package packet 6 | 7 | import ( 8 | "crypto" 9 | "encoding/binary" 10 | "io" 11 | "strconv" 12 | 13 | "github.com/ProtonMail/go-crypto/openpgp/errors" 14 | "github.com/ProtonMail/go-crypto/openpgp/internal/algorithm" 15 | ) 16 | 17 | // OnePassSignature represents a one-pass signature packet. See RFC 4880, 18 | // section 5.4. 19 | type OnePassSignature struct { 20 | Version int 21 | SigType SignatureType 22 | Hash crypto.Hash 23 | PubKeyAlgo PublicKeyAlgorithm 24 | KeyId uint64 25 | IsLast bool 26 | Salt []byte // v6 only 27 | KeyFingerprint []byte // v6 only 28 | } 29 | 30 | func (ops *OnePassSignature) parse(r io.Reader) (err error) { 31 | var buf [8]byte 32 | // Read: version | signature type | hash algorithm | public-key algorithm 33 | _, err = readFull(r, buf[:4]) 34 | if err != nil { 35 | return 36 | } 37 | if buf[0] != 3 && buf[0] != 6 { 38 | return errors.UnsupportedError("one-pass-signature packet version " + strconv.Itoa(int(buf[0]))) 39 | } 40 | ops.Version = int(buf[0]) 41 | 42 | var ok bool 43 | ops.Hash, ok = algorithm.HashIdToHashWithSha1(buf[2]) 44 | if !ok { 45 | return errors.UnsupportedError("hash function: " + strconv.Itoa(int(buf[2]))) 46 | } 47 | 48 | ops.SigType = SignatureType(buf[1]) 49 | ops.PubKeyAlgo = PublicKeyAlgorithm(buf[3]) 50 | 51 | if ops.Version == 6 { 52 | // Only for v6, a variable-length field containing the salt 53 | _, err = readFull(r, buf[:1]) 54 | if err != nil { 55 | return 56 | } 57 | saltLength := int(buf[0]) 58 | var expectedSaltLength int 59 | expectedSaltLength, err = SaltLengthForHash(ops.Hash) 60 | if err != nil { 61 | return 62 | } 63 | if saltLength != expectedSaltLength { 64 | err = errors.StructuralError("unexpected salt size for the given hash algorithm") 65 | return 66 | } 67 | salt := make([]byte, expectedSaltLength) 68 | _, err = readFull(r, salt) 69 | if err != nil { 70 | return 71 | } 72 | ops.Salt = salt 73 | 74 | // Only for v6 packets, 32 octets of the fingerprint of the signing key. 75 | fingerprint := make([]byte, 32) 76 | _, err = readFull(r, fingerprint) 77 | if err != nil { 78 | return 79 | } 80 | ops.KeyFingerprint = fingerprint 81 | ops.KeyId = binary.BigEndian.Uint64(ops.KeyFingerprint[:8]) 82 | } else { 83 | _, err = readFull(r, buf[:8]) 84 | if err != nil { 85 | return 86 | } 87 | ops.KeyId = binary.BigEndian.Uint64(buf[:8]) 88 | } 89 | 90 | _, err = readFull(r, buf[:1]) 91 | if err != nil { 92 | return 93 | } 94 | ops.IsLast = buf[0] != 0 95 | return 96 | } 97 | 98 | // Serialize marshals the given OnePassSignature to w. 99 | func (ops *OnePassSignature) Serialize(w io.Writer) error { 100 | //v3 length 1+1+1+1+8+1 = 101 | packetLength := 13 102 | if ops.Version == 6 { 103 | // v6 length 1+1+1+1+1+len(salt)+32+1 = 104 | packetLength = 38 + len(ops.Salt) 105 | } 106 | 107 | if err := serializeHeader(w, packetTypeOnePassSignature, packetLength); err != nil { 108 | return err 109 | } 110 | 111 | var buf [8]byte 112 | buf[0] = byte(ops.Version) 113 | buf[1] = uint8(ops.SigType) 114 | var ok bool 115 | buf[2], ok = algorithm.HashToHashIdWithSha1(ops.Hash) 116 | if !ok { 117 | return errors.UnsupportedError("hash type: " + strconv.Itoa(int(ops.Hash))) 118 | } 119 | buf[3] = uint8(ops.PubKeyAlgo) 120 | 121 | _, err := w.Write(buf[:4]) 122 | if err != nil { 123 | return err 124 | } 125 | 126 | if ops.Version == 6 { 127 | // write salt for v6 signatures 128 | _, err := w.Write([]byte{uint8(len(ops.Salt))}) 129 | if err != nil { 130 | return err 131 | } 132 | _, err = w.Write(ops.Salt) 133 | if err != nil { 134 | return err 135 | } 136 | 137 | // write fingerprint v6 signatures 138 | _, err = w.Write(ops.KeyFingerprint) 139 | if err != nil { 140 | return err 141 | } 142 | } else { 143 | binary.BigEndian.PutUint64(buf[:8], ops.KeyId) 144 | _, err := w.Write(buf[:8]) 145 | if err != nil { 146 | return err 147 | } 148 | } 149 | 150 | isLast := []byte{byte(0)} 151 | if ops.IsLast { 152 | isLast[0] = 1 153 | } 154 | 155 | _, err = w.Write(isLast) 156 | return err 157 | } 158 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | schedule: 9 | - cron: '43 11 * * 6' 10 | workflow_dispatch: 11 | 12 | jobs: 13 | analyze: 14 | name: Analyze (${{ matrix.language }}) 15 | # Runner size impacts CodeQL analysis time. To learn more, please see: 16 | # - https://gh.io/recommended-hardware-resources-for-running-codeql 17 | # - https://gh.io/supported-runners-and-hardware-resources 18 | # - https://gh.io/using-larger-runners (GitHub.com only) 19 | # Consider using larger runners or machines with greater resources for possible analysis time improvements. 20 | runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} 21 | permissions: 22 | # required for all workflows 23 | security-events: write 24 | 25 | # required to fetch internal or private CodeQL packs 26 | packages: read 27 | 28 | # only required for workflows in private repositories 29 | actions: read 30 | contents: read 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | include: 36 | - language: go 37 | build-mode: autobuild 38 | # CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' 39 | # Use `c-cpp` to analyze code written in C, C++ or both 40 | # Use 'java-kotlin' to analyze code written in Java, Kotlin or both 41 | # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both 42 | # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis, 43 | # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning. 44 | # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how 45 | # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages 46 | steps: 47 | - name: Checkout repository 48 | uses: actions/checkout@v4 49 | 50 | # Add any setup steps before running the `github/codeql-action/init` action. 51 | # This includes steps like installing compilers or runtimes (`actions/setup-node` 52 | # or others). This is typically only required for manual builds. 53 | # - name: Setup runtime (example) 54 | # uses: actions/setup-example@v1 55 | 56 | # Initializes the CodeQL tools for scanning. 57 | - name: Initialize CodeQL 58 | uses: github/codeql-action/init@v3 59 | with: 60 | languages: ${{ matrix.language }} 61 | build-mode: ${{ matrix.build-mode }} 62 | # If you wish to specify custom queries, you can do so here or in a config file. 63 | # By default, queries listed here will override any specified in a config file. 64 | # Prefix the list here with "+" to use these queries and those in the config file. 65 | 66 | # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 67 | # queries: security-extended,security-and-quality 68 | 69 | # If the analyze step fails for one of the languages you are analyzing with 70 | # "We were unable to automatically build your code", modify the matrix above 71 | # to set the build mode to "manual" for that language. Then modify this step 72 | # to build your code. 73 | # ℹ️ Command-line programs to run using the OS shell. 74 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 75 | - if: matrix.build-mode == 'manual' 76 | shell: bash 77 | run: | 78 | echo 'If you are using a "manual" build mode for one or more of the' \ 79 | 'languages you are analyzing, replace this with the commands to build' \ 80 | 'your code, for example:' 81 | echo ' make bootstrap' 82 | echo ' make release' 83 | exit 1 84 | 85 | - name: Perform CodeQL Analysis 86 | uses: github/codeql-action/analyze@v3 87 | with: 88 | category: "/language:${{matrix.language}}" 89 | -------------------------------------------------------------------------------- /openpgp/aes/keywrap/keywrap_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Matthew Endsley 2 | // All rights reserved 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted providing that the following conditions 6 | // are met: 7 | // 1. Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // 2. Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 14 | // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | // ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 17 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 21 | // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 22 | // IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 23 | // POSSIBILITY OF SUCH DAMAGE. 24 | 25 | package keywrap 26 | 27 | import ( 28 | "bytes" 29 | "testing" 30 | ) 31 | 32 | // A.1.8 - JSON Web Encryption 33 | func TestWrap(t *testing.T) { 34 | key := []byte{64, 154, 239, 170, 64, 40, 195, 99, 19, 84, 192, 142, 192, 238, 207, 217} 35 | sharedKey := []byte{25, 172, 32, 130, 225, 114, 26, 181, 138, 106, 254, 192, 95, 133, 74, 82} 36 | expectedWrappedKey := []byte{164, 255, 251, 1, 64, 200, 65, 200, 34, 197, 81, 143, 43, 211, 240, 38, 191, 161, 181, 117, 119, 68, 44, 80} 37 | 38 | wrappedKey, err := Wrap(sharedKey, key) 39 | if err != nil { 40 | t.Fatal("keywrap: failed to Wrap key: ", err) 41 | } 42 | 43 | if !bytes.Equal(expectedWrappedKey, wrappedKey) { 44 | t.Fatalf("unwrap: unexpected wrapped key:\n\t%v\n\t%v", expectedWrappedKey, wrappedKey) 45 | } 46 | } 47 | 48 | // A.1.8 - JSON Web Encryption 49 | func TestUnwrap(t *testing.T) { 50 | sharedKey := []byte{25, 172, 32, 130, 225, 114, 26, 181, 138, 106, 254, 192, 95, 133, 74, 82} 51 | wrappedKey := []byte{164, 255, 251, 1, 64, 200, 65, 200, 34, 197, 81, 143, 43, 211, 240, 38, 191, 161, 181, 117, 119, 68, 44, 80} 52 | expectedKey := []byte{64, 154, 239, 170, 64, 40, 195, 99, 19, 84, 192, 142, 192, 238, 207, 217} 53 | 54 | key, err := Unwrap(sharedKey, wrappedKey) 55 | if err != nil { 56 | t.Fatal("keywrap: failed to unwrap key: ", err) 57 | } 58 | 59 | if !bytes.Equal(expectedKey, key) { 60 | t.Fatalf("keywrap: unexpected wrapped key:\n\t%v\n\t%v", expectedKey, key) 61 | } 62 | } 63 | 64 | // Test wrap error cases. 65 | func TestWrapError(t *testing.T) { 66 | plaintext := make([]byte, 7) 67 | key := make([]byte, 32) 68 | _, err := Wrap(key, plaintext) 69 | if err != ErrWrapPlaintext { 70 | t.Fatalf("keywrap: expected Wrap to fail with %v, but have err=%v", ErrWrapPlaintext, err) 71 | } 72 | 73 | plaintext = append(plaintext, byte(0)) 74 | _, err = Wrap(key[:31], plaintext) 75 | if err != ErrInvalidKey { 76 | t.Fatalf("keywrap: expected Wrap to fail with %v, but have err=%v", ErrInvalidKey, err) 77 | } 78 | } 79 | 80 | // Test unwrap error cases. 81 | func TestUnwrapError(t *testing.T) { 82 | key := []byte{64, 154, 239, 170, 64, 40, 195, 99, 19, 84, 192, 142, 192, 238, 207, 217} 83 | sharedKey := []byte{25, 172, 32, 130, 225, 114, 26, 181, 138, 106, 254, 192, 95, 133, 74, 82} 84 | wrapped, err := Wrap(key, sharedKey) 85 | if err != nil { 86 | t.Fatalf("%v", err) 87 | } 88 | 89 | l := len(wrapped) 90 | _, err = Unwrap(key, wrapped[:l-2]) 91 | if err != ErrUnwrapCiphertext { 92 | t.Fatalf("keywrap: expected Unwrap to fail with %v, but have err=%v", ErrUnwrapCiphertext, err) 93 | } 94 | 95 | l = len(key) 96 | _, err = Unwrap(key[:l-2], wrapped) 97 | if err != ErrInvalidKey { 98 | t.Fatalf("keywrap: expected Unwrap to fail with %v, but have err=%v", ErrInvalidKey, err) 99 | } 100 | 101 | wrapped[0]-- 102 | _, err = Unwrap(key, wrapped) 103 | if err != ErrUnwrapFailed { 104 | t.Fatalf("keywrap: expected Unwrap to fail with %v, but have err=%v", ErrUnwrapFailed, err) 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /openpgp/s2k/s2k_config.go: -------------------------------------------------------------------------------- 1 | package s2k 2 | 3 | import "crypto" 4 | 5 | // Config collects configuration parameters for s2k key-stretching 6 | // transformations. A nil *Config is valid and results in all default 7 | // values. 8 | type Config struct { 9 | // S2K (String to Key) mode, used for key derivation in the context of secret key encryption 10 | // and passphrase-encrypted data. Either s2k.Argon2S2K or s2k.IteratedSaltedS2K may be used. 11 | // If the passphrase is a high-entropy key, indicated by setting PassphraseIsHighEntropy to true, 12 | // s2k.SaltedS2K can also be used. 13 | // Note: Argon2 is the strongest option but not all OpenPGP implementations are compatible with it 14 | //(pending standardisation). 15 | // 0 (simple), 1(salted), 3(iterated), 4(argon2) 16 | // 2(reserved) 100-110(private/experimental). 17 | S2KMode Mode 18 | // Only relevant if S2KMode is not set to s2k.Argon2S2K. 19 | // Hash is the default hash function to be used. If 20 | // nil, SHA256 is used. 21 | Hash crypto.Hash 22 | // Argon2 parameters for S2K (String to Key). 23 | // Only relevant if S2KMode is set to s2k.Argon2S2K. 24 | // If nil, default parameters are used. 25 | // For more details on the choice of parameters, see https://tools.ietf.org/html/rfc9106#section-4. 26 | Argon2Config *Argon2Config 27 | // Only relevant if S2KMode is set to s2k.IteratedSaltedS2K. 28 | // Iteration count for Iterated S2K (String to Key). It 29 | // determines the strength of the passphrase stretching when 30 | // the said passphrase is hashed to produce a key. S2KCount 31 | // should be between 65536 and 65011712, inclusive. If Config 32 | // is nil or S2KCount is 0, the value 16777216 used. Not all 33 | // values in the above range can be represented. S2KCount will 34 | // be rounded up to the next representable value if it cannot 35 | // be encoded exactly. When set, it is strongly encrouraged to 36 | // use a value that is at least 65536. See RFC 4880 Section 37 | // 3.7.1.3. 38 | S2KCount int 39 | // Indicates whether the passphrase passed by the application is a 40 | // high-entropy key (e.g. it's randomly generated or derived from 41 | // another passphrase using a strong key derivation function). 42 | // When true, allows the S2KMode to be s2k.SaltedS2K. 43 | // When the passphrase is not a high-entropy key, using SaltedS2K is 44 | // insecure, and not allowed by draft-ietf-openpgp-crypto-refresh-08. 45 | PassphraseIsHighEntropy bool 46 | } 47 | 48 | // Argon2Config stores the Argon2 parameters 49 | // A nil *Argon2Config is valid and results in all default 50 | type Argon2Config struct { 51 | NumberOfPasses uint8 52 | DegreeOfParallelism uint8 53 | // Memory specifies the desired Argon2 memory usage in kibibytes. 54 | // For example memory=64*1024 sets the memory cost to ~64 MB. 55 | Memory uint32 56 | } 57 | 58 | func (c *Config) Mode() Mode { 59 | if c == nil { 60 | return IteratedSaltedS2K 61 | } 62 | return c.S2KMode 63 | } 64 | 65 | func (c *Config) hash() crypto.Hash { 66 | if c == nil || uint(c.Hash) == 0 { 67 | return crypto.SHA256 68 | } 69 | 70 | return c.Hash 71 | } 72 | 73 | func (c *Config) Argon2() *Argon2Config { 74 | if c == nil || c.Argon2Config == nil { 75 | return nil 76 | } 77 | return c.Argon2Config 78 | } 79 | 80 | // EncodedCount get encoded count 81 | func (c *Config) EncodedCount() uint8 { 82 | if c == nil || c.S2KCount == 0 { 83 | return 224 // The common case. Corresponding to 16777216 84 | } 85 | 86 | i := c.S2KCount 87 | 88 | switch { 89 | case i < 65536: 90 | i = 65536 91 | case i > 65011712: 92 | i = 65011712 93 | } 94 | 95 | return encodeCount(i) 96 | } 97 | 98 | func (c *Argon2Config) Passes() uint8 { 99 | if c == nil || c.NumberOfPasses == 0 { 100 | return 3 101 | } 102 | return c.NumberOfPasses 103 | } 104 | 105 | func (c *Argon2Config) Parallelism() uint8 { 106 | if c == nil || c.DegreeOfParallelism == 0 { 107 | return 4 108 | } 109 | return c.DegreeOfParallelism 110 | } 111 | 112 | func (c *Argon2Config) EncodedMemory() uint8 { 113 | if c == nil || c.Memory == 0 { 114 | return 16 // 64 MiB of RAM 115 | } 116 | 117 | memory := c.Memory 118 | lowerBound := uint32(c.Parallelism()) * 8 119 | upperBound := uint32(2147483648) 120 | 121 | switch { 122 | case memory < lowerBound: 123 | memory = lowerBound 124 | case memory > upperBound: 125 | memory = upperBound 126 | } 127 | 128 | return encodeMemory(memory, c.Parallelism()) 129 | } 130 | -------------------------------------------------------------------------------- /openpgp/packet/userid.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package packet 6 | 7 | import ( 8 | "io" 9 | "strings" 10 | ) 11 | 12 | // UserId contains text that is intended to represent the name and email 13 | // address of the key holder. See RFC 4880, section 5.11. By convention, this 14 | // takes the form "Full Name (Comment) " 15 | type UserId struct { 16 | Id string // By convention, this takes the form "Full Name (Comment) " which is split out in the fields below. 17 | 18 | Name, Comment, Email string 19 | } 20 | 21 | func hasInvalidCharacters(s string) bool { 22 | for _, c := range s { 23 | switch c { 24 | case '(', ')', '<', '>', 0: 25 | return true 26 | } 27 | } 28 | return false 29 | } 30 | 31 | // NewUserId returns a UserId or nil if any of the arguments contain invalid 32 | // characters. The invalid characters are '\x00', '(', ')', '<' and '>' 33 | func NewUserId(name, comment, email string) *UserId { 34 | // RFC 4880 doesn't deal with the structure of userid strings; the 35 | // name, comment and email form is just a convention. However, there's 36 | // no convention about escaping the metacharacters and GPG just refuses 37 | // to create user ids where, say, the name contains a '('. We mirror 38 | // this behaviour. 39 | 40 | if hasInvalidCharacters(name) || hasInvalidCharacters(comment) || hasInvalidCharacters(email) { 41 | return nil 42 | } 43 | 44 | uid := new(UserId) 45 | uid.Name, uid.Comment, uid.Email = name, comment, email 46 | uid.Id = name 47 | if len(comment) > 0 { 48 | if len(uid.Id) > 0 { 49 | uid.Id += " " 50 | } 51 | uid.Id += "(" 52 | uid.Id += comment 53 | uid.Id += ")" 54 | } 55 | if len(email) > 0 { 56 | if len(uid.Id) > 0 { 57 | uid.Id += " " 58 | } 59 | uid.Id += "<" 60 | uid.Id += email 61 | uid.Id += ">" 62 | } 63 | return uid 64 | } 65 | 66 | func (uid *UserId) parse(r io.Reader) (err error) { 67 | // RFC 4880, section 5.11 68 | b, err := io.ReadAll(r) 69 | if err != nil { 70 | return 71 | } 72 | uid.Id = string(b) 73 | uid.Name, uid.Comment, uid.Email = parseUserId(uid.Id) 74 | return 75 | } 76 | 77 | // Serialize marshals uid to w in the form of an OpenPGP packet, including 78 | // header. 79 | func (uid *UserId) Serialize(w io.Writer) error { 80 | err := serializeHeader(w, packetTypeUserId, len(uid.Id)) 81 | if err != nil { 82 | return err 83 | } 84 | _, err = w.Write([]byte(uid.Id)) 85 | return err 86 | } 87 | 88 | // parseUserId extracts the name, comment and email from a user id string that 89 | // is formatted as "Full Name (Comment) ". 90 | func parseUserId(id string) (name, comment, email string) { 91 | var n, c, e struct { 92 | start, end int 93 | } 94 | var state int 95 | 96 | for offset, rune := range id { 97 | switch state { 98 | case 0: 99 | // Entering name 100 | n.start = offset 101 | state = 1 102 | fallthrough 103 | case 1: 104 | // In name 105 | if rune == '(' { 106 | state = 2 107 | n.end = offset 108 | } else if rune == '<' { 109 | state = 5 110 | n.end = offset 111 | } 112 | case 2: 113 | // Entering comment 114 | c.start = offset 115 | state = 3 116 | fallthrough 117 | case 3: 118 | // In comment 119 | if rune == ')' { 120 | state = 4 121 | c.end = offset 122 | } 123 | case 4: 124 | // Between comment and email 125 | if rune == '<' { 126 | state = 5 127 | } 128 | case 5: 129 | // Entering email 130 | e.start = offset 131 | state = 6 132 | fallthrough 133 | case 6: 134 | // In email 135 | if rune == '>' { 136 | state = 7 137 | e.end = offset 138 | } 139 | default: 140 | // After email 141 | } 142 | } 143 | switch state { 144 | case 1: 145 | // ended in the name 146 | n.end = len(id) 147 | case 3: 148 | // ended in comment 149 | c.end = len(id) 150 | case 6: 151 | // ended in email 152 | e.end = len(id) 153 | } 154 | 155 | name = strings.TrimSpace(id[n.start:n.end]) 156 | comment = strings.TrimSpace(id[c.start:c.end]) 157 | email = strings.TrimSpace(id[e.start:e.end]) 158 | 159 | // RFC 2822 3.4: alternate simple form of a mailbox 160 | if email == "" && strings.ContainsRune(name, '@') { 161 | email = name 162 | name = "" 163 | } 164 | 165 | return 166 | } 167 | -------------------------------------------------------------------------------- /openpgp/internal/algorithm/hash.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package algorithm 6 | 7 | import ( 8 | "crypto" 9 | "fmt" 10 | "hash" 11 | ) 12 | 13 | // Hash is an official hash function algorithm. See RFC 4880, section 9.4. 14 | type Hash interface { 15 | // Id returns the algorithm ID, as a byte, of Hash. 16 | Id() uint8 17 | // Available reports whether the given hash function is linked into the binary. 18 | Available() bool 19 | // HashFunc simply returns the value of h so that Hash implements SignerOpts. 20 | HashFunc() crypto.Hash 21 | // New returns a new hash.Hash calculating the given hash function. New 22 | // panics if the hash function is not linked into the binary. 23 | New() hash.Hash 24 | // Size returns the length, in bytes, of a digest resulting from the given 25 | // hash function. It doesn't require that the hash function in question be 26 | // linked into the program. 27 | Size() int 28 | // String is the name of the hash function corresponding to the given 29 | // OpenPGP hash id. 30 | String() string 31 | } 32 | 33 | // The following vars mirror the crypto/Hash supported hash functions. 34 | var ( 35 | SHA1 Hash = cryptoHash{2, crypto.SHA1} 36 | SHA256 Hash = cryptoHash{8, crypto.SHA256} 37 | SHA384 Hash = cryptoHash{9, crypto.SHA384} 38 | SHA512 Hash = cryptoHash{10, crypto.SHA512} 39 | SHA224 Hash = cryptoHash{11, crypto.SHA224} 40 | SHA3_256 Hash = cryptoHash{12, crypto.SHA3_256} 41 | SHA3_512 Hash = cryptoHash{14, crypto.SHA3_512} 42 | ) 43 | 44 | // HashById represents the different hash functions specified for OpenPGP. See 45 | // http://www.iana.org/assignments/pgp-parameters/pgp-parameters.xhtml#pgp-parameters-14 46 | var ( 47 | HashById = map[uint8]Hash{ 48 | SHA256.Id(): SHA256, 49 | SHA384.Id(): SHA384, 50 | SHA512.Id(): SHA512, 51 | SHA224.Id(): SHA224, 52 | SHA3_256.Id(): SHA3_256, 53 | SHA3_512.Id(): SHA3_512, 54 | } 55 | ) 56 | 57 | // cryptoHash contains pairs relating OpenPGP's hash identifier with 58 | // Go's crypto.Hash type. See RFC 4880, section 9.4. 59 | type cryptoHash struct { 60 | id uint8 61 | crypto.Hash 62 | } 63 | 64 | // Id returns the algorithm ID, as a byte, of cryptoHash. 65 | func (h cryptoHash) Id() uint8 { 66 | return h.id 67 | } 68 | 69 | var hashNames = map[uint8]string{ 70 | SHA256.Id(): "SHA256", 71 | SHA384.Id(): "SHA384", 72 | SHA512.Id(): "SHA512", 73 | SHA224.Id(): "SHA224", 74 | SHA3_256.Id(): "SHA3-256", 75 | SHA3_512.Id(): "SHA3-512", 76 | } 77 | 78 | func (h cryptoHash) String() string { 79 | s, ok := hashNames[h.id] 80 | if !ok { 81 | panic(fmt.Sprintf("Unsupported hash function %d", h.id)) 82 | } 83 | return s 84 | } 85 | 86 | // HashIdToHash returns a crypto.Hash which corresponds to the given OpenPGP 87 | // hash id. 88 | func HashIdToHash(id byte) (h crypto.Hash, ok bool) { 89 | if hash, ok := HashById[id]; ok { 90 | return hash.HashFunc(), true 91 | } 92 | return 0, false 93 | } 94 | 95 | // HashIdToHashWithSha1 returns a crypto.Hash which corresponds to the given OpenPGP 96 | // hash id, allowing sha1. 97 | func HashIdToHashWithSha1(id byte) (h crypto.Hash, ok bool) { 98 | if hash, ok := HashById[id]; ok { 99 | return hash.HashFunc(), true 100 | } 101 | 102 | if id == SHA1.Id() { 103 | return SHA1.HashFunc(), true 104 | } 105 | 106 | return 0, false 107 | } 108 | 109 | // HashIdToString returns the name of the hash function corresponding to the 110 | // given OpenPGP hash id. 111 | func HashIdToString(id byte) (name string, ok bool) { 112 | if hash, ok := HashById[id]; ok { 113 | return hash.String(), true 114 | } 115 | return "", false 116 | } 117 | 118 | // HashToHashId returns an OpenPGP hash id which corresponds the given Hash. 119 | func HashToHashId(h crypto.Hash) (id byte, ok bool) { 120 | for id, hash := range HashById { 121 | if hash.HashFunc() == h { 122 | return id, true 123 | } 124 | } 125 | 126 | return 0, false 127 | } 128 | 129 | // HashToHashIdWithSha1 returns an OpenPGP hash id which corresponds the given Hash, 130 | // allowing instances of SHA1 131 | func HashToHashIdWithSha1(h crypto.Hash) (id byte, ok bool) { 132 | for id, hash := range HashById { 133 | if hash.HashFunc() == h { 134 | return id, true 135 | } 136 | } 137 | 138 | if h == SHA1.HashFunc() { 139 | return SHA1.Id(), true 140 | } 141 | 142 | return 0, false 143 | } 144 | -------------------------------------------------------------------------------- /openpgp/internal/ecc/generic.go: -------------------------------------------------------------------------------- 1 | // Package ecc implements a generic interface for ECDH, ECDSA, and EdDSA. 2 | package ecc 3 | 4 | import ( 5 | "crypto/ecdsa" 6 | "crypto/elliptic" 7 | "fmt" 8 | "github.com/ProtonMail/go-crypto/openpgp/errors" 9 | "io" 10 | "math/big" 11 | ) 12 | 13 | type genericCurve struct { 14 | Curve elliptic.Curve 15 | } 16 | 17 | func NewGenericCurve(c elliptic.Curve) *genericCurve { 18 | return &genericCurve{ 19 | Curve: c, 20 | } 21 | } 22 | 23 | func (c *genericCurve) GetCurveName() string { 24 | return c.Curve.Params().Name 25 | } 26 | 27 | func (c *genericCurve) MarshalBytePoint(point []byte) []byte { 28 | return point 29 | } 30 | 31 | func (c *genericCurve) UnmarshalBytePoint(point []byte) []byte { 32 | return point 33 | } 34 | 35 | func (c *genericCurve) MarshalIntegerPoint(x, y *big.Int) []byte { 36 | return elliptic.Marshal(c.Curve, x, y) 37 | } 38 | 39 | func (c *genericCurve) UnmarshalIntegerPoint(point []byte) (x, y *big.Int) { 40 | return elliptic.Unmarshal(c.Curve, point) 41 | } 42 | 43 | func (c *genericCurve) MarshalByteSecret(d []byte) []byte { 44 | return d 45 | } 46 | 47 | func (c *genericCurve) UnmarshalByteSecret(d []byte) []byte { 48 | return d 49 | } 50 | 51 | func (c *genericCurve) MarshalIntegerSecret(d *big.Int) []byte { 52 | return d.Bytes() 53 | } 54 | 55 | func (c *genericCurve) UnmarshalIntegerSecret(d []byte) *big.Int { 56 | return new(big.Int).SetBytes(d) 57 | } 58 | 59 | func (c *genericCurve) GenerateECDH(rand io.Reader) (point, secret []byte, err error) { 60 | secret, x, y, err := elliptic.GenerateKey(c.Curve, rand) 61 | if err != nil { 62 | return nil, nil, err 63 | } 64 | 65 | point = elliptic.Marshal(c.Curve, x, y) 66 | return point, secret, nil 67 | } 68 | 69 | func (c *genericCurve) GenerateECDSA(rand io.Reader) (x, y, secret *big.Int, err error) { 70 | priv, err := ecdsa.GenerateKey(c.Curve, rand) 71 | if err != nil { 72 | return 73 | } 74 | 75 | return priv.X, priv.Y, priv.D, nil 76 | } 77 | 78 | func (c *genericCurve) Encaps(rand io.Reader, point []byte) (ephemeral, sharedSecret []byte, err error) { 79 | xP, yP := elliptic.Unmarshal(c.Curve, point) 80 | if xP == nil { 81 | panic("invalid point") 82 | } 83 | 84 | d, x, y, err := elliptic.GenerateKey(c.Curve, rand) 85 | if err != nil { 86 | return nil, nil, err 87 | } 88 | 89 | vsG := elliptic.Marshal(c.Curve, x, y) 90 | zbBig, _ := c.Curve.ScalarMult(xP, yP, d) 91 | 92 | byteLen := (c.Curve.Params().BitSize + 7) >> 3 93 | zb := make([]byte, byteLen) 94 | zbBytes := zbBig.Bytes() 95 | copy(zb[byteLen-len(zbBytes):], zbBytes) 96 | 97 | return vsG, zb, nil 98 | } 99 | 100 | func (c *genericCurve) Decaps(ephemeral, secret []byte) (sharedSecret []byte, err error) { 101 | x, y := elliptic.Unmarshal(c.Curve, ephemeral) 102 | zbBig, _ := c.Curve.ScalarMult(x, y, secret) 103 | byteLen := (c.Curve.Params().BitSize + 7) >> 3 104 | zb := make([]byte, byteLen) 105 | zbBytes := zbBig.Bytes() 106 | copy(zb[byteLen-len(zbBytes):], zbBytes) 107 | 108 | return zb, nil 109 | } 110 | 111 | func (c *genericCurve) Sign(rand io.Reader, x, y, d *big.Int, hash []byte) (r, s *big.Int, err error) { 112 | priv := &ecdsa.PrivateKey{D: d, PublicKey: ecdsa.PublicKey{X: x, Y: y, Curve: c.Curve}} 113 | return ecdsa.Sign(rand, priv, hash) 114 | } 115 | 116 | func (c *genericCurve) Verify(x, y *big.Int, hash []byte, r, s *big.Int) bool { 117 | pub := &ecdsa.PublicKey{X: x, Y: y, Curve: c.Curve} 118 | return ecdsa.Verify(pub, hash, r, s) 119 | } 120 | 121 | func (c *genericCurve) validate(xP, yP *big.Int, secret []byte) error { 122 | // the public point should not be at infinity (0,0) 123 | zero := new(big.Int) 124 | if xP.Cmp(zero) == 0 && yP.Cmp(zero) == 0 { 125 | return errors.KeyInvalidError(fmt.Sprintf("ecc (%s): infinity point", c.Curve.Params().Name)) 126 | } 127 | 128 | // re-derive the public point Q' = (X,Y) = dG 129 | // to compare to declared Q in public key 130 | expectedX, expectedY := c.Curve.ScalarBaseMult(secret) 131 | if xP.Cmp(expectedX) != 0 || yP.Cmp(expectedY) != 0 { 132 | return errors.KeyInvalidError(fmt.Sprintf("ecc (%s): invalid point", c.Curve.Params().Name)) 133 | } 134 | 135 | return nil 136 | } 137 | 138 | func (c *genericCurve) ValidateECDSA(xP, yP *big.Int, secret []byte) error { 139 | return c.validate(xP, yP, secret) 140 | } 141 | 142 | func (c *genericCurve) ValidateECDH(point []byte, secret []byte) error { 143 | xP, yP := elliptic.Unmarshal(c.Curve, point) 144 | if xP == nil { 145 | return errors.KeyInvalidError(fmt.Sprintf("ecc (%s): invalid point", c.Curve.Params().Name)) 146 | } 147 | 148 | return c.validate(xP, yP, secret) 149 | } 150 | -------------------------------------------------------------------------------- /openpgp/armor/armor.go: -------------------------------------------------------------------------------- 1 | // Copyright 2010 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package armor implements OpenPGP ASCII Armor, see RFC 4880. OpenPGP Armor is 6 | // very similar to PEM except that it has an additional CRC checksum. 7 | package armor // import "github.com/ProtonMail/go-crypto/openpgp/armor" 8 | 9 | import ( 10 | "bufio" 11 | "bytes" 12 | "encoding/base64" 13 | "io" 14 | 15 | "github.com/ProtonMail/go-crypto/openpgp/errors" 16 | ) 17 | 18 | // A Block represents an OpenPGP armored structure. 19 | // 20 | // The encoded form is: 21 | // 22 | // -----BEGIN Type----- 23 | // Headers 24 | // 25 | // base64-encoded Bytes 26 | // '=' base64 encoded checksum (optional) not checked anymore 27 | // -----END Type----- 28 | // 29 | // where Headers is a possibly empty sequence of Key: Value lines. 30 | // 31 | // Since the armored data can be very large, this package presents a streaming 32 | // interface. 33 | type Block struct { 34 | Type string // The type, taken from the preamble (i.e. "PGP SIGNATURE"). 35 | Header map[string]string // Optional headers. 36 | Body io.Reader // A Reader from which the contents can be read 37 | lReader lineReader 38 | oReader openpgpReader 39 | } 40 | 41 | var ArmorCorrupt error = errors.StructuralError("armor invalid") 42 | 43 | var armorStart = []byte("-----BEGIN ") 44 | var armorEnd = []byte("-----END ") 45 | var armorEndOfLine = []byte("-----") 46 | 47 | // lineReader wraps a line based reader. It watches for the end of an armor block 48 | type lineReader struct { 49 | in *bufio.Reader 50 | buf []byte 51 | eof bool 52 | } 53 | 54 | func (l *lineReader) Read(p []byte) (n int, err error) { 55 | if l.eof { 56 | return 0, io.EOF 57 | } 58 | 59 | if len(l.buf) > 0 { 60 | n = copy(p, l.buf) 61 | l.buf = l.buf[n:] 62 | return 63 | } 64 | 65 | line, isPrefix, err := l.in.ReadLine() 66 | if err != nil { 67 | return 68 | } 69 | if isPrefix { 70 | return 0, ArmorCorrupt 71 | } 72 | // Trim the line to remove any whitespace 73 | line = bytes.TrimSpace(line) 74 | 75 | if bytes.HasPrefix(line, armorEnd) { 76 | l.eof = true 77 | return 0, io.EOF 78 | } 79 | 80 | if len(line) == 5 && line[0] == '=' { 81 | // This is the checksum line 82 | // Don't check the checksum 83 | 84 | l.eof = true 85 | return 0, io.EOF 86 | } 87 | 88 | if len(line) > 96 { 89 | return 0, ArmorCorrupt 90 | } 91 | 92 | n = copy(p, line) 93 | bytesToSave := len(line) - n 94 | if bytesToSave > 0 { 95 | if cap(l.buf) < bytesToSave { 96 | l.buf = make([]byte, 0, bytesToSave) 97 | } 98 | l.buf = l.buf[0:bytesToSave] 99 | copy(l.buf, line[n:]) 100 | } 101 | 102 | return 103 | } 104 | 105 | // openpgpReader passes Read calls to the underlying base64 decoder. 106 | type openpgpReader struct { 107 | lReader *lineReader 108 | b64Reader io.Reader 109 | } 110 | 111 | func (r *openpgpReader) Read(p []byte) (n int, err error) { 112 | n, err = r.b64Reader.Read(p) 113 | return 114 | } 115 | 116 | // Decode reads a PGP armored block from the given Reader. It will ignore 117 | // leading garbage. If it doesn't find a block, it will return nil, io.EOF. The 118 | // given Reader is not usable after calling this function: an arbitrary amount 119 | // of data may have been read past the end of the block. 120 | func Decode(in io.Reader) (p *Block, err error) { 121 | r := bufio.NewReaderSize(in, 100) 122 | var line []byte 123 | ignoreNext := false 124 | 125 | TryNextBlock: 126 | p = nil 127 | 128 | // Skip leading garbage 129 | for { 130 | ignoreThis := ignoreNext 131 | line, ignoreNext, err = r.ReadLine() 132 | if err != nil { 133 | return 134 | } 135 | if ignoreNext || ignoreThis { 136 | continue 137 | } 138 | line = bytes.TrimSpace(line) 139 | if len(line) > len(armorStart)+len(armorEndOfLine) && bytes.HasPrefix(line, armorStart) { 140 | break 141 | } 142 | } 143 | 144 | p = new(Block) 145 | p.Type = string(line[len(armorStart) : len(line)-len(armorEndOfLine)]) 146 | p.Header = make(map[string]string) 147 | nextIsContinuation := false 148 | var lastKey string 149 | 150 | // Read headers 151 | for { 152 | isContinuation := nextIsContinuation 153 | line, nextIsContinuation, err = r.ReadLine() 154 | if err != nil { 155 | p = nil 156 | return 157 | } 158 | if isContinuation { 159 | p.Header[lastKey] += string(line) 160 | continue 161 | } 162 | line = bytes.TrimSpace(line) 163 | if len(line) == 0 { 164 | break 165 | } 166 | 167 | i := bytes.Index(line, []byte(":")) 168 | if i == -1 { 169 | goto TryNextBlock 170 | } 171 | lastKey = string(line[:i]) 172 | var value string 173 | if len(line) > i+2 { 174 | value = string(line[i+2:]) 175 | } 176 | p.Header[lastKey] = value 177 | } 178 | 179 | p.lReader.in = r 180 | p.oReader.lReader = &p.lReader 181 | p.oReader.b64Reader = base64.NewDecoder(base64.StdEncoding, &p.lReader) 182 | p.Body = &p.oReader 183 | 184 | return 185 | } 186 | -------------------------------------------------------------------------------- /openpgp/packet/opaque.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package packet 6 | 7 | import ( 8 | "bytes" 9 | "io" 10 | 11 | "github.com/ProtonMail/go-crypto/openpgp/errors" 12 | ) 13 | 14 | // OpaquePacket represents an OpenPGP packet as raw, unparsed data. This is 15 | // useful for splitting and storing the original packet contents separately, 16 | // handling unsupported packet types or accessing parts of the packet not yet 17 | // implemented by this package. 18 | type OpaquePacket struct { 19 | // Packet type 20 | Tag uint8 21 | // Reason why the packet was parsed opaquely 22 | Reason error 23 | // Binary contents of the packet data 24 | Contents []byte 25 | } 26 | 27 | func (op *OpaquePacket) parse(r io.Reader) (err error) { 28 | op.Contents, err = io.ReadAll(r) 29 | return 30 | } 31 | 32 | // Serialize marshals the packet to a writer in its original form, including 33 | // the packet header. 34 | func (op *OpaquePacket) Serialize(w io.Writer) (err error) { 35 | err = serializeHeader(w, packetType(op.Tag), len(op.Contents)) 36 | if err == nil { 37 | _, err = w.Write(op.Contents) 38 | } 39 | return 40 | } 41 | 42 | // Parse attempts to parse the opaque contents into a structure supported by 43 | // this package. If the packet is not known then the result will be another 44 | // OpaquePacket. 45 | func (op *OpaquePacket) Parse() (p Packet, err error) { 46 | hdr := bytes.NewBuffer(nil) 47 | err = serializeHeader(hdr, packetType(op.Tag), len(op.Contents)) 48 | if err != nil { 49 | op.Reason = err 50 | return op, err 51 | } 52 | p, err = Read(io.MultiReader(hdr, bytes.NewBuffer(op.Contents))) 53 | if err != nil { 54 | op.Reason = err 55 | p = op 56 | } 57 | return 58 | } 59 | 60 | // OpaqueReader reads OpaquePackets from an io.Reader. 61 | type OpaqueReader struct { 62 | r io.Reader 63 | } 64 | 65 | func NewOpaqueReader(r io.Reader) *OpaqueReader { 66 | return &OpaqueReader{r: r} 67 | } 68 | 69 | // Read the next OpaquePacket. 70 | func (or *OpaqueReader) Next() (op *OpaquePacket, err error) { 71 | tag, _, contents, err := readHeader(or.r) 72 | if err != nil { 73 | return 74 | } 75 | op = &OpaquePacket{Tag: uint8(tag), Reason: err} 76 | err = op.parse(contents) 77 | if err != nil { 78 | consumeAll(contents) 79 | } 80 | return 81 | } 82 | 83 | // OpaqueSubpacket represents an unparsed OpenPGP subpacket, 84 | // as found in signature and user attribute packets. 85 | type OpaqueSubpacket struct { 86 | SubType uint8 87 | EncodedLength []byte // Store the original encoded length for signature verifications. 88 | Contents []byte 89 | } 90 | 91 | // OpaqueSubpackets extracts opaque, unparsed OpenPGP subpackets from 92 | // their byte representation. 93 | func OpaqueSubpackets(contents []byte) (result []*OpaqueSubpacket, err error) { 94 | var ( 95 | subHeaderLen int 96 | subPacket *OpaqueSubpacket 97 | ) 98 | for len(contents) > 0 { 99 | subHeaderLen, subPacket, err = nextSubpacket(contents) 100 | if err != nil { 101 | break 102 | } 103 | result = append(result, subPacket) 104 | contents = contents[subHeaderLen+len(subPacket.Contents):] 105 | } 106 | return 107 | } 108 | 109 | func nextSubpacket(contents []byte) (subHeaderLen int, subPacket *OpaqueSubpacket, err error) { 110 | // RFC 4880, section 5.2.3.1 111 | var subLen uint32 112 | var encodedLength []byte 113 | if len(contents) < 1 { 114 | goto Truncated 115 | } 116 | subPacket = &OpaqueSubpacket{} 117 | switch { 118 | case contents[0] < 192: 119 | subHeaderLen = 2 // 1 length byte, 1 subtype byte 120 | if len(contents) < subHeaderLen { 121 | goto Truncated 122 | } 123 | encodedLength = contents[0:1] 124 | subLen = uint32(contents[0]) 125 | contents = contents[1:] 126 | case contents[0] < 255: 127 | subHeaderLen = 3 // 2 length bytes, 1 subtype 128 | if len(contents) < subHeaderLen { 129 | goto Truncated 130 | } 131 | encodedLength = contents[0:2] 132 | subLen = uint32(contents[0]-192)<<8 + uint32(contents[1]) + 192 133 | contents = contents[2:] 134 | default: 135 | subHeaderLen = 6 // 5 length bytes, 1 subtype 136 | if len(contents) < subHeaderLen { 137 | goto Truncated 138 | } 139 | encodedLength = contents[0:5] 140 | subLen = uint32(contents[1])<<24 | 141 | uint32(contents[2])<<16 | 142 | uint32(contents[3])<<8 | 143 | uint32(contents[4]) 144 | contents = contents[5:] 145 | 146 | } 147 | if subLen > uint32(len(contents)) || subLen == 0 { 148 | goto Truncated 149 | } 150 | subPacket.SubType = contents[0] 151 | subPacket.EncodedLength = encodedLength 152 | subPacket.Contents = contents[1:subLen] 153 | return 154 | Truncated: 155 | err = errors.StructuralError("subpacket truncated") 156 | return 157 | } 158 | 159 | func (osp *OpaqueSubpacket) Serialize(w io.Writer) (err error) { 160 | buf := make([]byte, 6) 161 | copy(buf, osp.EncodedLength) 162 | n := len(osp.EncodedLength) 163 | 164 | buf[n] = osp.SubType 165 | if _, err = w.Write(buf[:n+1]); err != nil { 166 | return 167 | } 168 | _, err = w.Write(osp.Contents) 169 | return 170 | } 171 | -------------------------------------------------------------------------------- /openpgp/aes/keywrap/keywrap.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Matthew Endsley 2 | // All rights reserved 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted providing that the following conditions 6 | // are met: 7 | // 1. Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // 2. Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 14 | // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | // ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 17 | // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 21 | // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 22 | // IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 23 | // POSSIBILITY OF SUCH DAMAGE. 24 | 25 | // Package keywrap is an implementation of the RFC 3394 AES key wrapping 26 | // algorithm. This is used in OpenPGP with elliptic curve keys. 27 | package keywrap 28 | 29 | import ( 30 | "crypto/aes" 31 | "encoding/binary" 32 | "errors" 33 | ) 34 | 35 | var ( 36 | // ErrWrapPlaintext is returned if the plaintext is not a multiple 37 | // of 64 bits. 38 | ErrWrapPlaintext = errors.New("keywrap: plainText must be a multiple of 64 bits") 39 | 40 | // ErrUnwrapCiphertext is returned if the ciphertext is not a 41 | // multiple of 64 bits. 42 | ErrUnwrapCiphertext = errors.New("keywrap: cipherText must by a multiple of 64 bits") 43 | 44 | // ErrUnwrapFailed is returned if unwrapping a key fails. 45 | ErrUnwrapFailed = errors.New("keywrap: failed to unwrap key") 46 | 47 | // NB: the AES NewCipher call only fails if the key is an invalid length. 48 | 49 | // ErrInvalidKey is returned when the AES key is invalid. 50 | ErrInvalidKey = errors.New("keywrap: invalid AES key") 51 | ) 52 | 53 | // Wrap a key using the RFC 3394 AES Key Wrap Algorithm. 54 | func Wrap(key, plainText []byte) ([]byte, error) { 55 | if len(plainText)%8 != 0 { 56 | return nil, ErrWrapPlaintext 57 | } 58 | 59 | c, err := aes.NewCipher(key) 60 | if err != nil { 61 | return nil, ErrInvalidKey 62 | } 63 | 64 | nblocks := len(plainText) / 8 65 | 66 | // 1) Initialize variables. 67 | var block [aes.BlockSize]byte 68 | // - Set A = IV, an initial value (see 2.2.3) 69 | for ii := 0; ii < 8; ii++ { 70 | block[ii] = 0xA6 71 | } 72 | 73 | // - For i = 1 to n 74 | // - Set R[i] = P[i] 75 | intermediate := make([]byte, len(plainText)) 76 | copy(intermediate, plainText) 77 | 78 | // 2) Calculate intermediate values. 79 | for ii := 0; ii < 6; ii++ { 80 | for jj := 0; jj < nblocks; jj++ { 81 | // - B = AES(K, A | R[i]) 82 | copy(block[8:], intermediate[jj*8:jj*8+8]) 83 | c.Encrypt(block[:], block[:]) 84 | 85 | // - A = MSB(64, B) ^ t where t = (n*j)+1 86 | t := uint64(ii*nblocks + jj + 1) 87 | val := binary.BigEndian.Uint64(block[:8]) ^ t 88 | binary.BigEndian.PutUint64(block[:8], val) 89 | 90 | // - R[i] = LSB(64, B) 91 | copy(intermediate[jj*8:jj*8+8], block[8:]) 92 | } 93 | } 94 | 95 | // 3) Output results. 96 | // - Set C[0] = A 97 | // - For i = 1 to n 98 | // - C[i] = R[i] 99 | return append(block[:8], intermediate...), nil 100 | } 101 | 102 | // Unwrap a key using the RFC 3394 AES Key Wrap Algorithm. 103 | func Unwrap(key, cipherText []byte) ([]byte, error) { 104 | if len(cipherText)%8 != 0 { 105 | return nil, ErrUnwrapCiphertext 106 | } 107 | 108 | c, err := aes.NewCipher(key) 109 | if err != nil { 110 | return nil, ErrInvalidKey 111 | } 112 | 113 | nblocks := len(cipherText)/8 - 1 114 | 115 | // 1) Initialize variables. 116 | var block [aes.BlockSize]byte 117 | // - Set A = C[0] 118 | copy(block[:8], cipherText[:8]) 119 | 120 | // - For i = 1 to n 121 | // - Set R[i] = C[i] 122 | intermediate := make([]byte, len(cipherText)-8) 123 | copy(intermediate, cipherText[8:]) 124 | 125 | // 2) Compute intermediate values. 126 | for jj := 5; jj >= 0; jj-- { 127 | for ii := nblocks - 1; ii >= 0; ii-- { 128 | // - B = AES-1(K, (A ^ t) | R[i]) where t = n*j+1 129 | // - A = MSB(64, B) 130 | t := uint64(jj*nblocks + ii + 1) 131 | val := binary.BigEndian.Uint64(block[:8]) ^ t 132 | binary.BigEndian.PutUint64(block[:8], val) 133 | 134 | copy(block[8:], intermediate[ii*8:ii*8+8]) 135 | c.Decrypt(block[:], block[:]) 136 | 137 | // - R[i] = LSB(B, 64) 138 | copy(intermediate[ii*8:ii*8+8], block[8:]) 139 | } 140 | } 141 | 142 | // 3) Output results. 143 | // - If A is an appropriate initial value (see 2.2.3), 144 | for ii := 0; ii < 8; ii++ { 145 | if block[ii] != 0xA6 { 146 | return nil, ErrUnwrapFailed 147 | } 148 | } 149 | 150 | // - For i = 1 to n 151 | // - P[i] = R[i] 152 | return intermediate, nil 153 | } 154 | -------------------------------------------------------------------------------- /openpgp/keys_v6_test.go: -------------------------------------------------------------------------------- 1 | package openpgp 2 | 3 | import ( 4 | "bytes" 5 | "crypto" 6 | "strings" 7 | "testing" 8 | 9 | "github.com/ProtonMail/go-crypto/openpgp/packet" 10 | ) 11 | 12 | var foreignKeysV6 = []string{ 13 | v6PrivKey, 14 | v6ArgonSealedPrivKey, 15 | } 16 | 17 | func TestReadPrivateForeignV6Key(t *testing.T) { 18 | for _, str := range foreignKeysV6 { 19 | kring, err := ReadArmoredKeyRing(strings.NewReader(str)) 20 | if err != nil { 21 | t.Fatal(err) 22 | } 23 | checkV6Key(t, kring[0]) 24 | } 25 | } 26 | 27 | func TestReadPrivateForeignV6KeyAndDecrypt(t *testing.T) { 28 | password := []byte("correct horse battery staple") 29 | kring, err := ReadArmoredKeyRing(strings.NewReader(v6ArgonSealedPrivKey)) 30 | if err != nil { 31 | t.Fatal(err) 32 | } 33 | key := kring[0] 34 | if key.PrivateKey != nil && key.PrivateKey.Encrypted { 35 | if err := key.PrivateKey.Decrypt(password); err != nil { 36 | t.Fatal(err) 37 | } 38 | } 39 | for _, sub := range key.Subkeys { 40 | if sub.PrivateKey != nil && sub.PrivateKey.Encrypted { 41 | if err := key.PrivateKey.Decrypt(password); err != nil { 42 | t.Fatal(err) 43 | } 44 | } 45 | } 46 | checkV6Key(t, kring[0]) 47 | } 48 | 49 | func TestReadPrivateEncryptedV6Key(t *testing.T) { 50 | c := &packet.Config{V6Keys: true} 51 | e, err := NewEntity("V6 Key Owner", "V6 Key", "v6@pm.me", c) 52 | if err != nil { 53 | t.Fatal(err) 54 | } 55 | password := []byte("test v6 key # password") 56 | // Encrypt private key 57 | if err = e.PrivateKey.Encrypt(password); err != nil { 58 | t.Fatal(err) 59 | } 60 | // Encrypt subkeys 61 | for _, sub := range e.Subkeys { 62 | if err = sub.PrivateKey.Encrypt(password); err != nil { 63 | t.Fatal(err) 64 | } 65 | } 66 | // Serialize, Read 67 | serializedEntity := bytes.NewBuffer(nil) 68 | err = e.SerializePrivateWithoutSigning(serializedEntity, nil) 69 | if err != nil { 70 | t.Fatal(err) 71 | } 72 | el, err := ReadKeyRing(serializedEntity) 73 | if err != nil { 74 | t.Fatal(err) 75 | } 76 | 77 | // Decrypt 78 | if el[0].PrivateKey == nil { 79 | t.Fatal("No private key found") 80 | } 81 | if err = el[0].PrivateKey.Decrypt(password); err != nil { 82 | t.Error(err) 83 | } 84 | 85 | // Decrypt subkeys 86 | for _, sub := range e.Subkeys { 87 | if err = sub.PrivateKey.Decrypt(password); err != nil { 88 | t.Error(err) 89 | } 90 | } 91 | 92 | checkV6Key(t, el[0]) 93 | } 94 | 95 | func TestNewEntitySerializeV6Key(t *testing.T) { 96 | c := &packet.Config{V6Keys: true} 97 | e, err := NewEntity("V6 Key Owner", "V6 Key", "v6@pm.me", c) 98 | if err != nil { 99 | t.Fatal(err) 100 | } 101 | checkSerializeReadv6(t, e) 102 | } 103 | 104 | func TestNewEntityV6Key(t *testing.T) { 105 | c := &packet.Config{ 106 | V6Keys: true, 107 | } 108 | e, err := NewEntity("V6 Key Owner", "V6 Key", "v6@pm.me", c) 109 | if err != nil { 110 | t.Fatal(err) 111 | } 112 | checkV6Key(t, e) 113 | } 114 | 115 | func checkV6Key(t *testing.T, ent *Entity) { 116 | key := ent.PrimaryKey 117 | if key.Version != 6 { 118 | t.Errorf("wrong key version %d", key.Version) 119 | } 120 | if len(key.Fingerprint) != 32 { 121 | t.Errorf("Wrong fingerprint length: %d", len(key.Fingerprint)) 122 | } 123 | signatures := ent.Revocations 124 | for _, id := range ent.Identities { 125 | signatures = append(signatures, id.SelfSignature) 126 | signatures = append(signatures, id.Signatures...) 127 | } 128 | for _, sig := range signatures { 129 | if sig == nil { 130 | continue 131 | } 132 | if sig.Version != 6 { 133 | t.Errorf("wrong signature version %d", sig.Version) 134 | } 135 | fgptLen := len(sig.IssuerFingerprint) 136 | if fgptLen != 32 { 137 | t.Errorf("Wrong fingerprint length in signature: %d", fgptLen) 138 | } 139 | } 140 | } 141 | 142 | func checkSerializeReadv6(t *testing.T, e *Entity) { 143 | // Entity serialize 144 | serializedEntity := bytes.NewBuffer(nil) 145 | err := e.Serialize(serializedEntity) 146 | if err != nil { 147 | t.Fatal(err) 148 | } 149 | el, err := ReadKeyRing(serializedEntity) 150 | if err != nil { 151 | t.Fatal(err) 152 | } 153 | checkV6Key(t, el[0]) 154 | 155 | // Without signing 156 | serializedEntity = bytes.NewBuffer(nil) 157 | err = e.SerializePrivateWithoutSigning(serializedEntity, nil) 158 | if err != nil { 159 | t.Fatal(err) 160 | } 161 | el, err = ReadKeyRing(serializedEntity) 162 | if err != nil { 163 | t.Fatal(err) 164 | } 165 | checkV6Key(t, el[0]) 166 | 167 | // Private 168 | serializedEntity = bytes.NewBuffer(nil) 169 | err = e.SerializePrivate(serializedEntity, nil) 170 | if err != nil { 171 | t.Fatal(err) 172 | } 173 | el, err = ReadKeyRing(serializedEntity) 174 | if err != nil { 175 | t.Fatal(err) 176 | } 177 | checkV6Key(t, el[0]) 178 | } 179 | 180 | func TestNewEntityWithDefaultHashv6(t *testing.T) { 181 | for _, hash := range hashes[:5] { 182 | c := &packet.Config{ 183 | V6Keys: true, 184 | DefaultHash: hash, 185 | } 186 | entity, err := NewEntity("Golang Gopher", "Test Key", "no-reply@golang.com", c) 187 | if hash == crypto.SHA1 { 188 | if err == nil { 189 | t.Fatal("should fail on SHA1 key creation") 190 | } 191 | continue 192 | } 193 | prefs := entity.SelfSignature.PreferredHash 194 | if prefs == nil { 195 | t.Fatal(err) 196 | } 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /eax/eax.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2019 ProtonTech AG 2 | 3 | // Package eax provides an implementation of the EAX 4 | // (encrypt-authenticate-translate) mode of operation, as described in 5 | // Bellare, Rogaway, and Wagner "THE EAX MODE OF OPERATION: A TWO-PASS 6 | // AUTHENTICATED-ENCRYPTION SCHEME OPTIMIZED FOR SIMPLICITY AND EFFICIENCY." 7 | // In FSE'04, volume 3017 of LNCS, 2004 8 | package eax 9 | 10 | import ( 11 | "crypto/cipher" 12 | "crypto/subtle" 13 | "errors" 14 | "github.com/ProtonMail/go-crypto/internal/byteutil" 15 | ) 16 | 17 | const ( 18 | defaultTagSize = 16 19 | defaultNonceSize = 16 20 | ) 21 | 22 | type eax struct { 23 | block cipher.Block // Only AES-{128, 192, 256} supported 24 | tagSize int // At least 12 bytes recommended 25 | nonceSize int 26 | } 27 | 28 | func (e *eax) NonceSize() int { 29 | return e.nonceSize 30 | } 31 | 32 | func (e *eax) Overhead() int { 33 | return e.tagSize 34 | } 35 | 36 | // NewEAX returns an EAX instance with AES-{KEYLENGTH} and default nonce and 37 | // tag lengths. Supports {128, 192, 256}- bit key length. 38 | func NewEAX(block cipher.Block) (cipher.AEAD, error) { 39 | return NewEAXWithNonceAndTagSize(block, defaultNonceSize, defaultTagSize) 40 | } 41 | 42 | // NewEAXWithNonceAndTagSize returns an EAX instance with AES-{keyLength} and 43 | // given nonce and tag lengths in bytes. Panics on zero nonceSize and 44 | // exceedingly long tags. 45 | // 46 | // It is recommended to use at least 12 bytes as tag length (see, for instance, 47 | // NIST SP 800-38D). 48 | // 49 | // Only to be used for compatibility with existing cryptosystems with 50 | // non-standard parameters. For all other cases, prefer NewEAX. 51 | func NewEAXWithNonceAndTagSize( 52 | block cipher.Block, nonceSize, tagSize int) (cipher.AEAD, error) { 53 | if nonceSize < 1 { 54 | return nil, eaxError("Cannot initialize EAX with nonceSize = 0") 55 | } 56 | if tagSize > block.BlockSize() { 57 | return nil, eaxError("Custom tag length exceeds blocksize") 58 | } 59 | return &eax{ 60 | block: block, 61 | tagSize: tagSize, 62 | nonceSize: nonceSize, 63 | }, nil 64 | } 65 | 66 | func (e *eax) Seal(dst, nonce, plaintext, adata []byte) []byte { 67 | if len(nonce) > e.nonceSize { 68 | panic("crypto/eax: Nonce too long for this instance") 69 | } 70 | ret, out := byteutil.SliceForAppend(dst, len(plaintext)+e.tagSize) 71 | omacNonce := e.omacT(0, nonce) 72 | omacAdata := e.omacT(1, adata) 73 | 74 | // Encrypt message using CTR mode and omacNonce as IV 75 | ctr := cipher.NewCTR(e.block, omacNonce) 76 | ciphertextData := out[:len(plaintext)] 77 | ctr.XORKeyStream(ciphertextData, plaintext) 78 | 79 | omacCiphertext := e.omacT(2, ciphertextData) 80 | 81 | tag := out[len(plaintext):] 82 | for i := 0; i < e.tagSize; i++ { 83 | tag[i] = omacCiphertext[i] ^ omacNonce[i] ^ omacAdata[i] 84 | } 85 | return ret 86 | } 87 | 88 | func (e *eax) Open(dst, nonce, ciphertext, adata []byte) ([]byte, error) { 89 | if len(nonce) > e.nonceSize { 90 | panic("crypto/eax: Nonce too long for this instance") 91 | } 92 | if len(ciphertext) < e.tagSize { 93 | return nil, eaxError("Ciphertext shorter than tag length") 94 | } 95 | sep := len(ciphertext) - e.tagSize 96 | 97 | // Compute tag 98 | omacNonce := e.omacT(0, nonce) 99 | omacAdata := e.omacT(1, adata) 100 | omacCiphertext := e.omacT(2, ciphertext[:sep]) 101 | 102 | tag := make([]byte, e.tagSize) 103 | for i := 0; i < e.tagSize; i++ { 104 | tag[i] = omacCiphertext[i] ^ omacNonce[i] ^ omacAdata[i] 105 | } 106 | 107 | // Compare tags 108 | if subtle.ConstantTimeCompare(ciphertext[sep:], tag) != 1 { 109 | return nil, eaxError("Tag authentication failed") 110 | } 111 | 112 | // Decrypt ciphertext 113 | ret, out := byteutil.SliceForAppend(dst, len(ciphertext)) 114 | ctr := cipher.NewCTR(e.block, omacNonce) 115 | ctr.XORKeyStream(out, ciphertext[:sep]) 116 | 117 | return ret[:sep], nil 118 | } 119 | 120 | // Tweakable OMAC - Calls OMAC_K([t]_n || plaintext) 121 | func (e *eax) omacT(t byte, plaintext []byte) []byte { 122 | blockSize := e.block.BlockSize() 123 | byteT := make([]byte, blockSize) 124 | byteT[blockSize-1] = t 125 | concat := append(byteT, plaintext...) 126 | return e.omac(concat) 127 | } 128 | 129 | func (e *eax) omac(plaintext []byte) []byte { 130 | blockSize := e.block.BlockSize() 131 | // L ← E_K(0^n); B ← 2L; P ← 4L 132 | L := make([]byte, blockSize) 133 | e.block.Encrypt(L, L) 134 | B := byteutil.GfnDouble(L) 135 | P := byteutil.GfnDouble(B) 136 | 137 | // CBC with IV = 0 138 | cbc := cipher.NewCBCEncrypter(e.block, make([]byte, blockSize)) 139 | padded := e.pad(plaintext, B, P) 140 | cbcCiphertext := make([]byte, len(padded)) 141 | cbc.CryptBlocks(cbcCiphertext, padded) 142 | 143 | return cbcCiphertext[len(cbcCiphertext)-blockSize:] 144 | } 145 | 146 | func (e *eax) pad(plaintext, B, P []byte) []byte { 147 | // if |M| in {n, 2n, 3n, ...} 148 | blockSize := e.block.BlockSize() 149 | if len(plaintext) != 0 && len(plaintext)%blockSize == 0 { 150 | return byteutil.RightXor(plaintext, B) 151 | } 152 | 153 | // else return (M || 1 || 0^(n−1−(|M| % n))) xor→ P 154 | ending := make([]byte, blockSize-len(plaintext)%blockSize) 155 | ending[0] = 0x80 156 | padded := append(plaintext, ending...) 157 | return byteutil.RightXor(padded, P) 158 | } 159 | 160 | func eaxError(err string) error { 161 | return errors.New("crypto/eax: " + err) 162 | } 163 | -------------------------------------------------------------------------------- /openpgp/armor/encode.go: -------------------------------------------------------------------------------- 1 | // Copyright 2010 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package armor 6 | 7 | import ( 8 | "encoding/base64" 9 | "io" 10 | "sort" 11 | ) 12 | 13 | var armorHeaderSep = []byte(": ") 14 | var blockEnd = []byte("\n=") 15 | var newline = []byte("\n") 16 | var armorEndOfLineOut = []byte("-----\n") 17 | 18 | const crc24Init = 0xb704ce 19 | const crc24Poly = 0x1864cfb 20 | 21 | // crc24 calculates the OpenPGP checksum as specified in RFC 4880, section 6.1 22 | func crc24(crc uint32, d []byte) uint32 { 23 | for _, b := range d { 24 | crc ^= uint32(b) << 16 25 | for i := 0; i < 8; i++ { 26 | crc <<= 1 27 | if crc&0x1000000 != 0 { 28 | crc ^= crc24Poly 29 | } 30 | } 31 | } 32 | return crc 33 | } 34 | 35 | // writeSlices writes its arguments to the given Writer. 36 | func writeSlices(out io.Writer, slices ...[]byte) (err error) { 37 | for _, s := range slices { 38 | _, err = out.Write(s) 39 | if err != nil { 40 | return err 41 | } 42 | } 43 | return 44 | } 45 | 46 | // lineBreaker breaks data across several lines, all of the same byte length 47 | // (except possibly the last). Lines are broken with a single '\n'. 48 | type lineBreaker struct { 49 | lineLength int 50 | line []byte 51 | used int 52 | out io.Writer 53 | haveWritten bool 54 | } 55 | 56 | func newLineBreaker(out io.Writer, lineLength int) *lineBreaker { 57 | return &lineBreaker{ 58 | lineLength: lineLength, 59 | line: make([]byte, lineLength), 60 | used: 0, 61 | out: out, 62 | } 63 | } 64 | 65 | func (l *lineBreaker) Write(b []byte) (n int, err error) { 66 | n = len(b) 67 | 68 | if n == 0 { 69 | return 70 | } 71 | 72 | if l.used == 0 && l.haveWritten { 73 | _, err = l.out.Write([]byte{'\n'}) 74 | if err != nil { 75 | return 76 | } 77 | } 78 | 79 | if l.used+len(b) < l.lineLength { 80 | l.used += copy(l.line[l.used:], b) 81 | return 82 | } 83 | 84 | l.haveWritten = true 85 | _, err = l.out.Write(l.line[0:l.used]) 86 | if err != nil { 87 | return 88 | } 89 | excess := l.lineLength - l.used 90 | l.used = 0 91 | 92 | _, err = l.out.Write(b[0:excess]) 93 | if err != nil { 94 | return 95 | } 96 | 97 | _, err = l.Write(b[excess:]) 98 | return 99 | } 100 | 101 | func (l *lineBreaker) Close() (err error) { 102 | if l.used > 0 { 103 | _, err = l.out.Write(l.line[0:l.used]) 104 | if err != nil { 105 | return 106 | } 107 | } 108 | 109 | return 110 | } 111 | 112 | // encoding keeps track of a running CRC24 over the data which has been written 113 | // to it and outputs a OpenPGP checksum when closed, followed by an armor 114 | // trailer. 115 | // 116 | // It's built into a stack of io.Writers: 117 | // 118 | // encoding -> base64 encoder -> lineBreaker -> out 119 | type encoding struct { 120 | out io.Writer 121 | breaker *lineBreaker 122 | b64 io.WriteCloser 123 | crc uint32 124 | crcEnabled bool 125 | blockType []byte 126 | } 127 | 128 | func (e *encoding) Write(data []byte) (n int, err error) { 129 | if e.crcEnabled { 130 | e.crc = crc24(e.crc, data) 131 | } 132 | return e.b64.Write(data) 133 | } 134 | 135 | func (e *encoding) Close() (err error) { 136 | err = e.b64.Close() 137 | if err != nil { 138 | return 139 | } 140 | e.breaker.Close() 141 | 142 | if e.crcEnabled { 143 | var checksumBytes [3]byte 144 | checksumBytes[0] = byte(e.crc >> 16) 145 | checksumBytes[1] = byte(e.crc >> 8) 146 | checksumBytes[2] = byte(e.crc) 147 | 148 | var b64ChecksumBytes [4]byte 149 | base64.StdEncoding.Encode(b64ChecksumBytes[:], checksumBytes[:]) 150 | 151 | return writeSlices(e.out, blockEnd, b64ChecksumBytes[:], newline, armorEnd, e.blockType, armorEndOfLine) 152 | } 153 | return writeSlices(e.out, newline, armorEnd, e.blockType, armorEndOfLine) 154 | } 155 | 156 | func encode(out io.Writer, blockType string, headers map[string]string, checksum bool) (w io.WriteCloser, err error) { 157 | bType := []byte(blockType) 158 | err = writeSlices(out, armorStart, bType, armorEndOfLineOut) 159 | if err != nil { 160 | return 161 | } 162 | 163 | keys := make([]string, len(headers)) 164 | i := 0 165 | for k := range headers { 166 | keys[i] = k 167 | i++ 168 | } 169 | sort.Strings(keys) 170 | for _, k := range keys { 171 | err = writeSlices(out, []byte(k), armorHeaderSep, []byte(headers[k]), newline) 172 | if err != nil { 173 | return 174 | } 175 | } 176 | 177 | _, err = out.Write(newline) 178 | if err != nil { 179 | return 180 | } 181 | 182 | e := &encoding{ 183 | out: out, 184 | breaker: newLineBreaker(out, 64), 185 | blockType: bType, 186 | crc: crc24Init, 187 | crcEnabled: checksum, 188 | } 189 | e.b64 = base64.NewEncoder(base64.StdEncoding, e.breaker) 190 | return e, nil 191 | } 192 | 193 | // Encode returns a WriteCloser which will encode the data written to it in 194 | // OpenPGP armor. 195 | func Encode(out io.Writer, blockType string, headers map[string]string) (w io.WriteCloser, err error) { 196 | return encode(out, blockType, headers, true) 197 | } 198 | 199 | // EncodeWithChecksumOption returns a WriteCloser which will encode the data written to it in 200 | // OpenPGP armor and provides the option to include a checksum. 201 | // When forming ASCII Armor, the CRC24 footer SHOULD NOT be generated, 202 | // unless interoperability with implementations that require the CRC24 footer 203 | // to be present is a concern. 204 | func EncodeWithChecksumOption(out io.Writer, blockType string, headers map[string]string, doChecksum bool) (w io.WriteCloser, err error) { 205 | return encode(out, blockType, headers, doChecksum) 206 | } 207 | --------------------------------------------------------------------------------