github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbcrypto/kbpackets.go (about) 1 // Copyright 2018 Keybase, Inc. All rights reserved. Use of 2 // this source code is governed by the included BSD license. 3 4 package kbcrypto 5 6 // Code for encoding and decoding Keybase packet types. 7 8 import ( 9 "bytes" 10 "crypto/sha256" 11 "encoding/base64" 12 "errors" 13 "fmt" 14 15 "github.com/keybase/go-codec/codec" 16 ) 17 18 type PacketVersion int 19 20 const ( 21 KeybasePacketV1 PacketVersion = 1 22 ) 23 24 // PacketTag are tags for OpenPGP and Keybase packets. It is a uint to 25 // be backwards compatible with older versions of codec that encoded 26 // positive ints as uints. 27 type PacketTag uint 28 29 const ( 30 TagP3skb PacketTag = 513 31 TagSignature PacketTag = 514 32 TagEncryption PacketTag = 515 33 ) 34 35 func (t PacketTag) String() string { 36 switch t { 37 case TagP3skb: 38 return "PacketTag(P3skb)" 39 case TagSignature: 40 return "PacketTag(Signature)" 41 case TagEncryption: 42 return "PacketTag(Encryption)" 43 default: 44 return fmt.Sprintf("PacketTag(%d)", uint(t)) 45 } 46 } 47 48 type Packetable interface { 49 GetTagAndVersion() (PacketTag, PacketVersion) 50 } 51 52 func EncodePacket(p Packetable, encoder *codec.Encoder) error { 53 packet, err := newKeybasePacket(p, true) 54 if err != nil { 55 return err 56 } 57 return encoder.Encode(packet) 58 } 59 60 func EncodePacketToBytes(p Packetable) ([]byte, error) { 61 packet, err := newKeybasePacket(p, true) 62 if err != nil { 63 return nil, err 64 } 65 return packet.encode() 66 } 67 68 func EncodePacketToBytesWithOptionalHash(p Packetable, doHash bool) ([]byte, error) { 69 packet, err := newKeybasePacket(p, doHash) 70 if err != nil { 71 return nil, err 72 } 73 return packet.encode() 74 } 75 76 func EncodePacketToArmoredString(p Packetable) (string, error) { 77 packet, err := newKeybasePacket(p, true) 78 if err != nil { 79 return "", err 80 } 81 return packet.armoredEncode() 82 } 83 84 type UnmarshalError struct { 85 ExpectedTag PacketTag 86 Tag PacketTag 87 } 88 89 func (u UnmarshalError) Error() string { 90 return fmt.Sprintf("Expected %s packet, got %s packet", u.ExpectedTag, u.Tag) 91 } 92 93 func DecodePacket(decoder *codec.Decoder, body Packetable) error { 94 // TODO: Do something with the version too? 95 tag, _ := body.GetTagAndVersion() 96 p := keybasePacket{ 97 Body: body, 98 } 99 err := decoder.Decode(&p) 100 if err != nil { 101 return err 102 } 103 104 if p.Tag != tag { 105 return UnmarshalError{ExpectedTag: p.Tag, Tag: tag} 106 } 107 108 // TODO: Figure out a way to do the same reencode check as in 109 // DecodePacketFromBytes. 110 111 return p.checkHash() 112 } 113 114 func DecodePacketFromBytes(data []byte, body Packetable) error { 115 ch := CodecHandle() 116 decoder := codec.NewDecoderBytes(data, ch) 117 118 // TODO: Do something with the version too? 119 tag, _ := body.GetTagAndVersion() 120 p := keybasePacket{ 121 Body: body, 122 } 123 err := decoder.Decode(&p) 124 if err != nil { 125 return err 126 } 127 128 if decoder.NumBytesRead() != len(data) { 129 return fmt.Errorf("Did not consume entire buffer: %d byte(s) left", len(data)-decoder.NumBytesRead()) 130 } 131 132 if p.Tag != tag { 133 return UnmarshalError{ExpectedTag: p.Tag, Tag: tag} 134 } 135 136 // Test for nonstandard msgpack data (which could be maliciously crafted) 137 // by re-encoding and making sure we get the same thing. 138 // https://github.com/keybase/client/issues/423 139 // 140 // Ideally this should be done at a lower level, but our 141 // msgpack library doesn't sort maps the way we expect. See 142 // https://github.com/ugorji/go/issues/103 143 if reencoded, err := p.encode(); err != nil { 144 return err 145 } else if !bytes.Equal(reencoded, data) { 146 return FishyMsgpackError{data, reencoded} 147 } 148 149 return p.checkHash() 150 } 151 152 type FishyMsgpackError struct { 153 original []byte 154 reencoded []byte 155 } 156 157 func (e FishyMsgpackError) Error() string { 158 return fmt.Sprintf("Original msgpack data didn't match re-encoded version: reencoded=%x != original=%x", e.reencoded, e.original) 159 } 160 161 func CodecHandle() *codec.MsgpackHandle { 162 var mh codec.MsgpackHandle 163 mh.WriteExt = true 164 return &mh 165 } 166 167 const SHA256Code = 8 168 169 type keybasePacketHash struct { 170 Type int `codec:"type"` 171 Value []byte `codec:"value"` 172 } 173 174 type keybasePacket struct { 175 Body Packetable `codec:"body"` 176 Hash *keybasePacketHash `codec:"hash,omitempty"` 177 Tag PacketTag `codec:"tag"` 178 Version PacketVersion `codec:"version"` 179 } 180 181 // newKeybasePacket creates a new keybase crypto packet, optionally computing a 182 // hash over all data in the packet (via doHash). Every client 1.0.17 and above 183 // provides this flag (implicitly, since before it wasn't optional). Some 1.0.16 184 // clients did this, and no clients 1.0.15 and earlier did it. We use the flag 185 // so that we can generate the legacy hashes for old 1.0.16 186 func newKeybasePacket(body Packetable, doHash bool) (*keybasePacket, error) { 187 tag, version := body.GetTagAndVersion() 188 ret := keybasePacket{ 189 Body: body, 190 Tag: tag, 191 Version: version, 192 } 193 if doHash { 194 ret.Hash = &keybasePacketHash{ 195 Type: SHA256Code, 196 Value: []byte{}, 197 } 198 hashBytes, hashErr := ret.hashSum() 199 if hashErr != nil { 200 return nil, hashErr 201 } 202 ret.Hash.Value = hashBytes 203 } 204 return &ret, nil 205 } 206 207 func (p *keybasePacket) hashToBytes() ([]byte, error) { 208 // We don't include the Hash field in the encoded bytes that we hash, 209 // because if we did then the result wouldn't be stable. To work around 210 // that, we make a copy of the packet and overwrite the Hash field with 211 // an empty slice. 212 packetCopy := *p 213 packetCopy.Hash = &keybasePacketHash{ 214 Type: SHA256Code, 215 Value: []byte{}, 216 } 217 return packetCopy.hashSum() 218 } 219 220 func (p *keybasePacket) hashSum() ([]byte, error) { 221 if len(p.Hash.Value) != 0 { 222 return nil, errors.New("cannot compute hash with Value present") 223 } 224 encoded, err := p.encode() 225 if err != nil { 226 return nil, err 227 } 228 ret := sha256.Sum256(encoded) 229 return ret[:], nil 230 } 231 232 func (p *keybasePacket) checkHash() error { 233 var gotten []byte 234 var err error 235 if p.Hash == nil { 236 return nil 237 } 238 given := p.Hash.Value 239 if p.Hash.Type != SHA256Code { 240 err = fmt.Errorf("Bad hash code: %d", p.Hash.Type) 241 } else if gotten, err = p.hashToBytes(); err != nil { 242 243 } else if !FastByteArrayEq(gotten, given) { 244 err = fmt.Errorf("Bad packet hash") 245 } 246 return err 247 } 248 249 func (p *keybasePacket) encode() ([]byte, error) { 250 var encoded []byte 251 err := codec.NewEncoderBytes(&encoded, CodecHandle()).Encode(p) 252 return encoded, err 253 } 254 255 func (p *keybasePacket) armoredEncode() (string, error) { 256 var buf bytes.Buffer 257 err := func() (err error) { 258 b64 := base64.NewEncoder(base64.StdEncoding, &buf) 259 defer func() { 260 closeErr := b64.Close() 261 if err == nil { 262 err = closeErr 263 } 264 }() 265 encoder := codec.NewEncoder(b64, CodecHandle()) 266 return encoder.Encode(p) 267 }() 268 if err != nil { 269 return "", err 270 } 271 return buf.String(), nil 272 }