github.com/letsencrypt/boulder@v0.20251208.0/grpc/pb-marshalling.go (about) 1 // Copyright 2016 ISRG. All rights reserved 2 // This Source Code Form is subject to the terms of the Mozilla Public 3 // License, v. 2.0. If a copy of the MPL was not distributed with this 4 // file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 6 package grpc 7 8 import ( 9 "fmt" 10 "net/netip" 11 "time" 12 13 "github.com/go-jose/go-jose/v4" 14 "google.golang.org/grpc/codes" 15 "google.golang.org/protobuf/types/known/timestamppb" 16 17 "github.com/letsencrypt/boulder/core" 18 corepb "github.com/letsencrypt/boulder/core/proto" 19 "github.com/letsencrypt/boulder/identifier" 20 "github.com/letsencrypt/boulder/probs" 21 sapb "github.com/letsencrypt/boulder/sa/proto" 22 vapb "github.com/letsencrypt/boulder/va/proto" 23 ) 24 25 var ErrMissingParameters = CodedError(codes.FailedPrecondition, "required RPC parameter was missing") 26 var ErrInvalidParameters = CodedError(codes.InvalidArgument, "RPC parameter was invalid") 27 28 // This file defines functions to translate between the protobuf types and the 29 // code types. 30 31 func ProblemDetailsToPB(prob *probs.ProblemDetails) (*corepb.ProblemDetails, error) { 32 if prob == nil { 33 // nil problemDetails is valid 34 return nil, nil 35 } 36 return &corepb.ProblemDetails{ 37 ProblemType: string(prob.Type), 38 Detail: prob.Detail, 39 HttpStatus: int32(prob.HTTPStatus), //nolint: gosec // HTTP status codes are guaranteed to be small, no risk of overflow. 40 }, nil 41 } 42 43 func PBToProblemDetails(in *corepb.ProblemDetails) (*probs.ProblemDetails, error) { 44 if in == nil { 45 // nil problemDetails is valid 46 return nil, nil 47 } 48 if in.ProblemType == "" || in.Detail == "" { 49 return nil, ErrMissingParameters 50 } 51 prob := &probs.ProblemDetails{ 52 Type: probs.ProblemType(in.ProblemType), 53 Detail: in.Detail, 54 } 55 if in.HttpStatus != 0 { 56 prob.HTTPStatus = int(in.HttpStatus) 57 } 58 return prob, nil 59 } 60 61 func ChallengeToPB(challenge core.Challenge) (*corepb.Challenge, error) { 62 prob, err := ProblemDetailsToPB(challenge.Error) 63 if err != nil { 64 return nil, err 65 } 66 recordAry := make([]*corepb.ValidationRecord, len(challenge.ValidationRecord)) 67 for i, v := range challenge.ValidationRecord { 68 recordAry[i], err = ValidationRecordToPB(v) 69 if err != nil { 70 return nil, err 71 } 72 } 73 74 var validated *timestamppb.Timestamp 75 if challenge.Validated != nil { 76 validated = timestamppb.New(challenge.Validated.UTC()) 77 if !validated.IsValid() { 78 return nil, fmt.Errorf("error creating *timestamppb.Timestamp for *corepb.Challenge object") 79 } 80 } 81 82 return &corepb.Challenge{ 83 Type: string(challenge.Type), 84 Status: string(challenge.Status), 85 Token: challenge.Token, 86 Error: prob, 87 Validationrecords: recordAry, 88 Validated: validated, 89 }, nil 90 } 91 92 func PBToChallenge(in *corepb.Challenge) (challenge core.Challenge, err error) { 93 if in == nil { 94 return core.Challenge{}, ErrMissingParameters 95 } 96 if in.Type == "" || in.Status == "" || in.Token == "" { 97 return core.Challenge{}, ErrMissingParameters 98 } 99 var recordAry []core.ValidationRecord 100 if len(in.Validationrecords) > 0 { 101 recordAry = make([]core.ValidationRecord, len(in.Validationrecords)) 102 for i, v := range in.Validationrecords { 103 recordAry[i], err = PBToValidationRecord(v) 104 if err != nil { 105 return core.Challenge{}, err 106 } 107 } 108 } 109 prob, err := PBToProblemDetails(in.Error) 110 if err != nil { 111 return core.Challenge{}, err 112 } 113 var validated *time.Time 114 if !core.IsAnyNilOrZero(in.Validated) { 115 val := in.Validated.AsTime() 116 validated = &val 117 } 118 ch := core.Challenge{ 119 Type: core.AcmeChallenge(in.Type), 120 Status: core.AcmeStatus(in.Status), 121 Token: in.Token, 122 Error: prob, 123 ValidationRecord: recordAry, 124 Validated: validated, 125 } 126 return ch, nil 127 } 128 129 func ValidationRecordToPB(record core.ValidationRecord) (*corepb.ValidationRecord, error) { 130 addrs := make([][]byte, len(record.AddressesResolved)) 131 addrsTried := make([][]byte, len(record.AddressesTried)) 132 var err error 133 for i, v := range record.AddressesResolved { 134 addrs[i] = v.AsSlice() 135 } 136 for i, v := range record.AddressesTried { 137 addrsTried[i] = v.AsSlice() 138 } 139 addrUsed, err := record.AddressUsed.MarshalText() 140 if err != nil { 141 return nil, err 142 } 143 return &corepb.ValidationRecord{ 144 Hostname: record.Hostname, 145 Port: record.Port, 146 AddressesResolved: addrs, 147 AddressUsed: addrUsed, 148 Url: record.URL, 149 AddressesTried: addrsTried, 150 ResolverAddrs: record.ResolverAddrs, 151 }, nil 152 } 153 154 func PBToValidationRecord(in *corepb.ValidationRecord) (record core.ValidationRecord, err error) { 155 if in == nil { 156 return core.ValidationRecord{}, ErrMissingParameters 157 } 158 addrs := make([]netip.Addr, len(in.AddressesResolved)) 159 for i, v := range in.AddressesResolved { 160 netIP, ok := netip.AddrFromSlice(v) 161 if !ok { 162 return core.ValidationRecord{}, ErrInvalidParameters 163 } 164 addrs[i] = netIP 165 } 166 addrsTried := make([]netip.Addr, len(in.AddressesTried)) 167 for i, v := range in.AddressesTried { 168 netIP, ok := netip.AddrFromSlice(v) 169 if !ok { 170 return core.ValidationRecord{}, ErrInvalidParameters 171 } 172 addrsTried[i] = netIP 173 } 174 var addrUsed netip.Addr 175 err = addrUsed.UnmarshalText(in.AddressUsed) 176 if err != nil { 177 return 178 } 179 return core.ValidationRecord{ 180 Hostname: in.Hostname, 181 Port: in.Port, 182 AddressesResolved: addrs, 183 AddressUsed: addrUsed, 184 URL: in.Url, 185 AddressesTried: addrsTried, 186 ResolverAddrs: in.ResolverAddrs, 187 }, nil 188 } 189 190 func ValidationResultToPB(records []core.ValidationRecord, prob *probs.ProblemDetails, perspective, rir string) (*vapb.ValidationResult, error) { 191 recordAry := make([]*corepb.ValidationRecord, len(records)) 192 var err error 193 for i, v := range records { 194 recordAry[i], err = ValidationRecordToPB(v) 195 if err != nil { 196 return nil, err 197 } 198 } 199 marshalledProb, err := ProblemDetailsToPB(prob) 200 if err != nil { 201 return nil, err 202 } 203 return &vapb.ValidationResult{ 204 Records: recordAry, 205 Problem: marshalledProb, 206 Perspective: perspective, 207 Rir: rir, 208 }, nil 209 } 210 211 func pbToValidationResult(in *vapb.ValidationResult) ([]core.ValidationRecord, *probs.ProblemDetails, error) { 212 if in == nil { 213 return nil, nil, ErrMissingParameters 214 } 215 recordAry := make([]core.ValidationRecord, len(in.Records)) 216 var err error 217 for i, v := range in.Records { 218 recordAry[i], err = PBToValidationRecord(v) 219 if err != nil { 220 return nil, nil, err 221 } 222 } 223 prob, err := PBToProblemDetails(in.Problem) 224 if err != nil { 225 return nil, nil, err 226 } 227 return recordAry, prob, nil 228 } 229 230 func CAAResultToPB(prob *probs.ProblemDetails, perspective, rir string) (*vapb.IsCAAValidResponse, error) { 231 marshalledProb, err := ProblemDetailsToPB(prob) 232 if err != nil { 233 return nil, err 234 } 235 return &vapb.IsCAAValidResponse{ 236 Problem: marshalledProb, 237 Perspective: perspective, 238 Rir: rir, 239 }, nil 240 } 241 242 func RegistrationToPB(reg core.Registration) (*corepb.Registration, error) { 243 keyBytes, err := reg.Key.MarshalJSON() 244 if err != nil { 245 return nil, err 246 } 247 var createdAt *timestamppb.Timestamp 248 if reg.CreatedAt != nil { 249 createdAt = timestamppb.New(reg.CreatedAt.UTC()) 250 if !createdAt.IsValid() { 251 return nil, fmt.Errorf("error creating *timestamppb.Timestamp for *corepb.Authorization object") 252 } 253 } 254 255 return &corepb.Registration{ 256 Id: reg.ID, 257 Key: keyBytes, 258 Agreement: reg.Agreement, 259 CreatedAt: createdAt, 260 Status: string(reg.Status), 261 }, nil 262 } 263 264 func PbToRegistration(pb *corepb.Registration) (core.Registration, error) { 265 var key jose.JSONWebKey 266 err := key.UnmarshalJSON(pb.Key) 267 if err != nil { 268 return core.Registration{}, err 269 } 270 var createdAt *time.Time 271 if !core.IsAnyNilOrZero(pb.CreatedAt) { 272 c := pb.CreatedAt.AsTime() 273 createdAt = &c 274 } 275 return core.Registration{ 276 ID: pb.Id, 277 Key: &key, 278 Agreement: pb.Agreement, 279 CreatedAt: createdAt, 280 Status: core.AcmeStatus(pb.Status), 281 }, nil 282 } 283 284 func AuthzToPB(authz core.Authorization) (*corepb.Authorization, error) { 285 challs := make([]*corepb.Challenge, len(authz.Challenges)) 286 for i, c := range authz.Challenges { 287 pbChall, err := ChallengeToPB(c) 288 if err != nil { 289 return nil, err 290 } 291 challs[i] = pbChall 292 } 293 var expires *timestamppb.Timestamp 294 if authz.Expires != nil { 295 expires = timestamppb.New(authz.Expires.UTC()) 296 if !expires.IsValid() { 297 return nil, fmt.Errorf("error creating *timestamppb.Timestamp for *corepb.Authorization object") 298 } 299 } 300 301 return &corepb.Authorization{ 302 Id: authz.ID, 303 Identifier: authz.Identifier.ToProto(), 304 RegistrationID: authz.RegistrationID, 305 Status: string(authz.Status), 306 Expires: expires, 307 Challenges: challs, 308 CertificateProfileName: authz.CertificateProfileName, 309 }, nil 310 } 311 312 func PBToAuthz(pb *corepb.Authorization) (core.Authorization, error) { 313 challs := make([]core.Challenge, len(pb.Challenges)) 314 for i, c := range pb.Challenges { 315 chall, err := PBToChallenge(c) 316 if err != nil { 317 return core.Authorization{}, err 318 } 319 challs[i] = chall 320 } 321 var expires *time.Time 322 if !core.IsAnyNilOrZero(pb.Expires) { 323 c := pb.Expires.AsTime() 324 expires = &c 325 } 326 authz := core.Authorization{ 327 ID: pb.Id, 328 Identifier: identifier.FromProto(pb.Identifier), 329 RegistrationID: pb.RegistrationID, 330 Status: core.AcmeStatus(pb.Status), 331 Expires: expires, 332 Challenges: challs, 333 CertificateProfileName: pb.CertificateProfileName, 334 } 335 return authz, nil 336 } 337 338 // orderValid checks that a corepb.Order is valid. In addition to the checks 339 // from `newOrderValid` it ensures the order ID and the Created fields are not 340 // the zero value. 341 func orderValid(order *corepb.Order) bool { 342 return order.Id != 0 && order.Created != nil && newOrderValid(order) 343 } 344 345 // newOrderValid checks that a corepb.Order is valid. It allows for a nil 346 // `order.Id` because the order has not been assigned an ID yet when it is being 347 // created initially. It allows `order.BeganProcessing` to be nil because 348 // `sa.NewOrder` explicitly sets it to the default value. It allows 349 // `order.Created` to be nil because the SA populates this. It also allows 350 // `order.CertificateSerial` to be nil such that it can be used in places where 351 // the order has not been finalized yet. 352 func newOrderValid(order *corepb.Order) bool { 353 return !(order.RegistrationID == 0 || order.Expires == nil || len(order.Identifiers) == 0) 354 } 355 356 // PBToAuthzMap converts a protobuf map of identifiers mapped to protobuf 357 // authorizations to a golang map[string]*core.Authorization. 358 func PBToAuthzMap(pb *sapb.Authorizations) (map[identifier.ACMEIdentifier]*core.Authorization, error) { 359 m := make(map[identifier.ACMEIdentifier]*core.Authorization, len(pb.Authzs)) 360 for _, v := range pb.Authzs { 361 authz, err := PBToAuthz(v) 362 if err != nil { 363 return nil, err 364 } 365 m[authz.Identifier] = &authz 366 } 367 return m, nil 368 }