1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
|
diff --git a/openpgp/read.go b/openpgp/read.go
index a6cecc5..0c9397b 100644
--- a/openpgp/read.go
+++ b/openpgp/read.go
@@ -56,8 +56,9 @@ type MessageDetails struct {
// been consumed. Once EOF has been seen, the following fields are
// valid. (An authentication code failure is reported as a
// SignatureError error when reading from UnverifiedBody.)
- SignatureError error // nil if the signature is good.
- Signature *packet.Signature // the signature packet itself.
+ SignatureError error // nil if the signature is good.
+ Signature *packet.Signature // the signature packet itself, if v4 (default)
+ SignatureV3 *packet.SignatureV3 // the signature packet if it is a v2 or v3 signature
decrypted io.ReadCloser
}
@@ -334,13 +335,15 @@ func (scr *signatureCheckReader) Read(buf []byte) (n int, err error) {
}
var ok bool
- if scr.md.Signature, ok = p.(*packet.Signature); !ok {
+ if scr.md.Signature, ok = p.(*packet.Signature); ok {
+ scr.md.SignatureError = scr.md.SignedBy.PublicKey.VerifySignature(scr.h, scr.md.Signature)
+ } else if scr.md.SignatureV3, ok = p.(*packet.SignatureV3); ok {
+ scr.md.SignatureError = scr.md.SignedBy.PublicKey.VerifySignatureV3(scr.h, scr.md.SignatureV3)
+ } else {
scr.md.SignatureError = errors.StructuralError("LiteralData not followed by Signature")
return
}
- scr.md.SignatureError = scr.md.SignedBy.PublicKey.VerifySignature(scr.h, scr.md.Signature)
-
// The SymmetricallyEncrypted packet, if any, might have an
// unsigned hash of its own. In order to check this we need to
// close that Reader.
diff --git a/openpgp/read_test.go b/openpgp/read_test.go
index 52f942c..abe8d7b 100644
--- a/openpgp/read_test.go
+++ b/openpgp/read_test.go
@@ -13,6 +13,7 @@ import (
"strings"
"testing"
+ "golang.org/x/crypto/openpgp/armor"
"golang.org/x/crypto/openpgp/errors"
)
@@ -411,6 +412,50 @@ func TestIssue11504(t *testing.T) {
testReadMessageError(t, "9303000130303030303030303030983002303030303030030000000130")
}
+// TestSignatureV3Message tests the verification of V3 signature, generated
+// with a modern V4-style key. Some people have their clients set to generate
+// V3 signatures, so it's useful to be able to verify them.
+func TestSignatureV3Message(t *testing.T) {
+ sig, err := armor.Decode(strings.NewReader(signedMessageV3))
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ key, err := ReadArmoredKeyRing(strings.NewReader(keyV4forVerifyingSignedMessageV3))
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ md, err := ReadMessage(sig.Body, key, nil, nil)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ _, err = ioutil.ReadAll(md.UnverifiedBody)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ // We'll see a sig error here after reading in the UnverifiedBody above,
+ // if there was one to see.
+ if err = md.SignatureError; err != nil {
+ t.Error(err)
+ return
+ }
+
+ if md.SignatureV3 == nil {
+ t.Errorf("No available signature after checking signature")
+ return
+ }
+ if md.Signature != nil {
+ t.Errorf("Did not expect a signature V4 back")
+ return
+ }
+ return
+}
+
const testKey1KeyId = 0xA34D7E18C20C31BB
const testKey3KeyId = 0x338934250CCC0360
@@ -504,3 +549,36 @@ const unknownHashFunctionHex = `8a00000040040001990006050253863c24000a09103b4fe6
const missingHashFunctionHex = `8a00000040040001030006050253863c24000a09103b4fe6acc0b21f32ffff0101010101010101010101010101010101010101010101010101010101010101010101010101`
const campbellQuine = `a0b001000300fcffa0b001000d00f2ff000300fcffa0b001000d00f2ff8270a01c00000500faff8270a01c00000500faff000500faff001400ebff8270a01c00000500faff000500faff001400ebff428821c400001400ebff428821c400001400ebff428821c400001400ebff428821c400001400ebff428821c400000000ffff000000ffff000b00f4ff428821c400000000ffff000000ffff000b00f4ff0233214c40000100feff000233214c40000100feff0000`
+
+const keyV4forVerifyingSignedMessageV3 = `-----BEGIN PGP PUBLIC KEY BLOCK-----
+Comment: GPGTools - https://gpgtools.org
+
+mI0EVfxoFQEEAMBIqmbDfYygcvP6Phr1wr1XI41IF7Qixqybs/foBF8qqblD9gIY
+BKpXjnBOtbkcVOJ0nljd3/sQIfH4E0vQwK5/4YRQSI59eKOqd6Fx+fWQOLG+uu6z
+tewpeCj9LLHvibx/Sc7VWRnrznia6ftrXxJ/wHMezSab3tnGC0YPVdGNABEBAAG0
+JEdvY3J5cHRvIFRlc3QgS2V5IDx0aGVtYXhAZ21haWwuY29tPoi5BBMBCgAjBQJV
+/GgVAhsDBwsJCAcDAgEGFQgCCQoLBBYCAwECHgECF4AACgkQeXnQmhdGW9PFVAP+
+K7TU0qX5ArvIONIxh/WAweyOk884c5cE8f+3NOPOOCRGyVy0FId5A7MmD5GOQh4H
+JseOZVEVCqlmngEvtHZb3U1VYtVGE5WZ+6rQhGsMcWP5qaT4soYwMBlSYxgYwQcx
+YhN9qOr292f9j2Y//TTIJmZT4Oa+lMxhWdqTfX+qMgG4jQRV/GgVAQQArhFSiij1
+b+hT3dnapbEU+23Z1yTu1DfF6zsxQ4XQWEV3eR8v+8mEDDNcz8oyyF56k6UQ3rXi
+UMTIwRDg4V6SbZmaFbZYCOwp/EmXJ3rfhm7z7yzXj2OFN22luuqbyVhuL7LRdB0M
+pxgmjXb4tTvfgKd26x34S+QqUJ7W6uprY4sAEQEAAYifBBgBCgAJBQJV/GgVAhsM
+AAoJEHl50JoXRlvT7y8D/02ckx4OMkKBZo7viyrBw0MLG92i+DC2bs35PooHR6zz
+786mitjOp5z2QWNLBvxC70S0qVfCIz8jKupO1J6rq6Z8CcbLF3qjm6h1omUBf8Nd
+EfXKD2/2HV6zMKVknnKzIEzauh+eCKS2CeJUSSSryap/QLVAjRnckaES/OsEWhNB
+=RZia
+-----END PGP PUBLIC KEY BLOCK-----
+`
+
+const signedMessageV3 = `-----BEGIN PGP MESSAGE-----
+Comment: GPGTools - https://gpgtools.org
+
+owGbwMvMwMVYWXlhlrhb9GXG03JJDKF/MtxDMjKLFYAoUaEktbhEITe1uDgxPVWP
+q5NhKjMrWAVcC9evD8z/bF/uWNjqtk/X3y5/38XGRQHm/57rrDRYuGnTw597Xqka
+uM3137/hH3Os+Jf2dc0fXOITKwJvXJvecPVs0ta+Vg7ZO1MLn8w58Xx+6L58mbka
+DGHyU9yTueZE8D+QF/Tz28Y78dqtF56R1VPn9Xw4uJqrWYdd7b3vIZ1V6R4Nh05d
+iT57d/OhWwA=
+=hG7R
+-----END PGP MESSAGE-----
+`
|