github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbcrypto/nacl_sig_info.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 import ( 7 "encoding/base64" 8 "errors" 9 "fmt" 10 11 keybase1 "github.com/keybase/client/go/protocol/keybase1" 12 ) 13 14 type NaclSigInfo struct { 15 Kid keybase1.BinaryKID `codec:"key"` 16 Payload []byte `codec:"payload,omitempty"` 17 Sig NaclSignature `codec:"sig"` 18 SigType AlgoType `codec:"sig_type"` 19 HashType HashType `codec:"hash_type"` 20 Detached bool `codec:"detached"` 21 Version int `codec:"version,omitempty"` 22 Prefix SignaturePrefix `codec:"prefix,omitempty"` 23 } 24 25 func (s *NaclSigInfo) GetTagAndVersion() (PacketTag, PacketVersion) { 26 return TagSignature, KeybasePacketV1 27 } 28 29 var _ Packetable = (*NaclSigInfo)(nil) 30 31 type BadKeyError struct { 32 Msg string 33 } 34 35 func (p BadKeyError) Error() string { 36 msg := "Bad key found" 37 if len(p.Msg) != 0 { 38 msg = msg + ": " + p.Msg 39 } 40 return msg 41 } 42 43 type VerificationError struct { 44 // XXX - NOTE(maxtaco) - 20190418 - this is not to be confused with Cause(), which interacts with the pkg/errors 45 // system. There should probably be a better solution than this, but let's leave it for now. 46 Cause error 47 } 48 49 func newVerificationErrorWithString(s string) VerificationError { 50 return VerificationError{Cause: errors.New(s)} 51 } 52 53 func NewVerificationError(e error) VerificationError { 54 return VerificationError{Cause: e} 55 } 56 57 func (e VerificationError) Error() string { 58 if e.Cause == nil { 59 return "Verification failed" 60 } 61 return fmt.Sprintf("Verification failed: %s", e.Cause.Error()) 62 } 63 64 func (e VerificationError) ToStatus() keybase1.Status { 65 cause := "" 66 if e.Cause != nil { 67 cause = e.Cause.Error() 68 } 69 return keybase1.Status{ 70 Code: SCSigCannotVerify, 71 Name: "SC_SIG_CANNOT_VERIFY", 72 Fields: []keybase1.StringKVPair{ 73 {Key: "Cause", Value: cause}, 74 }, 75 } 76 } 77 78 type UnhandledSignatureError struct { 79 version int 80 } 81 82 func (e UnhandledSignatureError) Error() string { 83 return fmt.Sprintf("unhandled signature version: %d", e.version) 84 } 85 86 func (s NaclSigInfo) Verify() (*NaclSigningKeyPublic, error) { 87 return s.verifyWithPayload(s.Payload, false) 88 } 89 90 // verifyWithPayload verifies the NaclSigInfo s, with the payload payload. Note that 91 // s may or may not have a payload already baked into it. If it does, and checkPayloadEquality 92 // is true, then we assert that the "baked-in" payload is equal to the specified payload. 93 // We'll only pass `false` for this flag from just above, to avoid checking that s.Payload == s.Payload, 94 // which we know it does. We need this unfortunate complexity because some signatures the client 95 // tries to verify are "attached," meaning the payload comes along with the sig info. And in other 96 // cases, the signatures are "detached", meaning they are supplied out-of-band. This function 97 // handles both cases. 98 func (s NaclSigInfo) verifyWithPayload(payload []byte, checkPayloadEquality bool) (*NaclSigningKeyPublic, error) { 99 key := KIDToNaclSigningKeyPublic(s.Kid) 100 if key == nil { 101 return nil, BadKeyError{} 102 } 103 if payload == nil { 104 return nil, newVerificationErrorWithString("nil payload") 105 } 106 if len(payload) == 0 { 107 return nil, newVerificationErrorWithString("empty payload") 108 } 109 110 if checkPayloadEquality && s.Payload != nil && !SecureByteArrayEq(payload, s.Payload) { 111 return nil, newVerificationErrorWithString("payload mismatch") 112 } 113 114 switch s.Version { 115 case 0, 1: 116 if !key.Verify(payload, s.Sig) { 117 return nil, newVerificationErrorWithString("verify failed") 118 } 119 case 2: 120 if !s.Prefix.IsWhitelisted() { 121 return nil, newVerificationErrorWithString("unknown prefix") 122 } 123 if !key.Verify(s.Prefix.Prefix(payload), s.Sig) { 124 return nil, newVerificationErrorWithString("verify failed") 125 } 126 default: 127 return nil, UnhandledSignatureError{s.Version} 128 } 129 130 return key, nil 131 } 132 133 func (s *NaclSigInfo) ArmoredEncode() (ret string, err error) { 134 return EncodePacketToArmoredString(s) 135 } 136 137 func DecodeNaclSigInfoPacket(data []byte) (NaclSigInfo, error) { 138 var info NaclSigInfo 139 err := DecodePacketFromBytes(data, &info) 140 if err != nil { 141 return NaclSigInfo{}, err 142 } 143 return info, nil 144 } 145 146 func DecodeArmoredNaclSigInfoPacket(s string) (NaclSigInfo, error) { 147 b, err := base64.StdEncoding.DecodeString(s) 148 if err != nil { 149 return NaclSigInfo{}, err 150 } 151 return DecodeNaclSigInfoPacket(b) 152 } 153 154 // NaclVerifyAndExtract interprets the given string as a NaCl-signed messaged, in 155 // the keybase NaclSigInfo (v1) format. It will check that the signature verified, and if so, 156 // will return the public key that was used for the verification, the payload of the signature, 157 // the full body of the decoded SignInfo, and an error 158 func NaclVerifyAndExtract(s string) (nk *NaclSigningKeyPublic, payload []byte, fullBody []byte, err error) { 159 fullBody, err = base64.StdEncoding.DecodeString(s) 160 if err != nil { 161 return nil, nil, nil, err 162 } 163 164 naclSig, err := DecodeNaclSigInfoPacket(fullBody) 165 if err != nil { 166 return nil, nil, nil, err 167 } 168 169 nk, err = naclSig.Verify() 170 if err != nil { 171 return nil, nil, nil, err 172 } 173 174 payload = naclSig.Payload 175 return nk, payload, fullBody, nil 176 } 177 178 func NaclVerifyWithPayload(sig string, payloadIn []byte) (nk *NaclSigningKeyPublic, fullBody []byte, err error) { 179 fullBody, err = base64.StdEncoding.DecodeString(sig) 180 if err != nil { 181 return nil, nil, err 182 } 183 184 naclSig, err := DecodeNaclSigInfoPacket(fullBody) 185 if err != nil { 186 return nil, nil, err 187 } 188 189 nk, err = naclSig.verifyWithPayload(payloadIn, true) 190 if err != nil { 191 return nil, nil, err 192 } 193 194 return nk, fullBody, nil 195 } 196 197 func (s NaclSigInfo) SigID() (ret keybase1.SigIDBase, err error) { 198 var body []byte 199 body, err = EncodePacketToBytes(&s) 200 if err != nil { 201 return "", err 202 } 203 return ComputeSigIDFromSigBody(body), nil 204 }