main
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
5package packet
6
7import (
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.
19type 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
30func (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.
99func (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}