summaryrefslogtreecommitdiffstats
path: root/vendor/golang.org/x/crypto/openpgp/packet/signature_v3.go
blob: 6edff889349b3f50df3f866c82c55277b0b07565 (plain) (blame)
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
136
137
138
139
140
141
142
143
144
145
146
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package packet

import (
	"crypto"
	"encoding/binary"
	"fmt"
	"io"
	"strconv"
	"time"

	"golang.org/x/crypto/openpgp/errors"
	"golang.org/x/crypto/openpgp/s2k"
)

// SignatureV3 represents older version 3 signatures. These signatures are less secure
// than version 4 and should not be used to create new signatures. They are included
// here for backwards compatibility to read and validate with older key material.
// See RFC 4880, section 5.2.2.
type SignatureV3 struct {
	SigType      SignatureType
	CreationTime time.Time
	IssuerKeyId  uint64
	PubKeyAlgo   PublicKeyAlgorithm
	Hash         crypto.Hash
	HashTag      [2]byte

	RSASignature     parsedMPI
	DSASigR, DSASigS parsedMPI
}

func (sig *SignatureV3) parse(r io.Reader) (err error) {
	// RFC 4880, section 5.2.2
	var buf [8]byte
	if _, err = readFull(r, buf[:1]); err != nil {
		return
	}
	if buf[0] < 2 || buf[0] > 3 {
		err = errors.UnsupportedError("signature packet version " + strconv.Itoa(int(buf[0])))
		return
	}
	if _, err = readFull(r, buf[:1]); err != nil {
		return
	}
	if buf[0] != 5 {
		err = errors.UnsupportedError(
			"invalid hashed material length " + strconv.Itoa(int(buf[0])))
		return
	}

	// Read hashed material: signature type + creation time
	if _, err = readFull(r, buf[:5]); err != nil {
		return
	}
	sig.SigType = SignatureType(buf[0])
	t := binary.BigEndian.Uint32(buf[1:5])
	sig.CreationTime = time.Unix(int64(t), 0)

	// Eight-octet Key ID of signer.
	if _, err = readFull(r, buf[:8]); err != nil {
		return
	}
	sig.IssuerKeyId = binary.BigEndian.Uint64(buf[:])

	// Public-key and hash algorithm
	if _, err = readFull(r, buf[:2]); err != nil {
		return
	}
	sig.PubKeyAlgo = PublicKeyAlgorithm(buf[0])
	switch sig.PubKeyAlgo {
	case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoDSA:
	default:
		err = errors.UnsupportedError("public key algorithm " + strconv.Itoa(int(sig.PubKeyAlgo)))
		return
	}
	var ok bool
	if sig.Hash, ok = s2k.HashIdToHash(buf[1]); !ok {
		return errors.UnsupportedError("hash function " + strconv.Itoa(int(buf[2])))
	}

	// Two-octet field holding left 16 bits of signed hash value.
	if _, err = readFull(r, sig.HashTag[:2]); err != nil {
		return
	}

	switch sig.PubKeyAlgo {
	case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
		sig.RSASignature.bytes, sig.RSASignature.bitLength, err = readMPI(r)
	case PubKeyAlgoDSA:
		if sig.DSASigR.bytes, sig.DSASigR.bitLength, err = readMPI(r); err != nil {
			return
		}
		sig.DSASigS.bytes, sig.DSASigS.bitLength, err = readMPI(r)
	default:
		panic("unreachable")
	}
	return
}

// Serialize marshals sig to w. Sign, SignUserId or SignKey must have been
// called first.
func (sig *SignatureV3) Serialize(w io.Writer) (err error) {
	buf := make([]byte, 8)

	// Write the sig type and creation time
	buf[0] = byte(sig.SigType)
	binary.BigEndian.PutUint32(buf[1:5], uint32(sig.CreationTime.Unix()))
	if _, err = w.Write(buf[:5]); err != nil {
		return
	}

	// Write the issuer long key ID
	binary.BigEndian.PutUint64(buf[:8], sig.IssuerKeyId)
	if _, err = w.Write(buf[:8]); err != nil {
		return
	}

	// Write public key algorithm, hash ID, and hash value
	buf[0] = byte(sig.PubKeyAlgo)
	hashId, ok := s2k.HashToHashId(sig.Hash)
	if !ok {
		return errors.UnsupportedError(fmt.Sprintf("hash function %v", sig.Hash))
	}
	buf[1] = hashId
	copy(buf[2:4], sig.HashTag[:])
	if _, err = w.Write(buf[:4]); err != nil {
		return
	}

	if sig.RSASignature.bytes == nil && sig.DSASigR.bytes == nil {
		return errors.InvalidArgumentError("Signature: need to call Sign, SignUserId or SignKey before Serialize")
	}

	switch sig.PubKeyAlgo {
	case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
		err = writeMPIs(w, sig.RSASignature)
	case PubKeyAlgoDSA:
		err = writeMPIs(w, sig.DSASigR, sig.DSASigS)
	default:
		panic("impossible")
	}
	return
}