@@ -71,44 +71,77 @@ func (m *MessageCertificate13) Marshal() ([]byte, error) {
7171 return nil , errCertificateRequestContextTooLong
7272 }
7373
74+ out := make ([]byte , m .Size ())
75+ err := m .MarshalInto (out )
76+ return out , err
77+ }
78+
79+ func (m * MessageCertificate13 ) Size () int {
80+ return 1 + len (m .CertificateRequestContext ) + cert13CertLengthFieldSize + m .certsSize ()
81+ }
82+
83+ func (m * MessageCertificate13 ) certsSize () int {
84+ certificateListSize := 0
85+ for _ , entry := range m .CertificateList {
86+ certificateListSize += cert13CertLengthFieldSize
87+ certificateListSize += len (entry .CertificateData )
88+ certificateListSize += cert13ExtLengthFieldSize
89+ certificateListSize += extension .Size (entry .Extensions )
90+ }
91+ return certificateListSize
92+ }
93+
94+ // MarshalInto, same as Marshal but uses a pre-allocated buffer.
95+ func (m * MessageCertificate13 ) MarshalInto (out []byte ) error {
96+ // Validate certificate_request_context length
97+ if len (m .CertificateRequestContext ) > cert13ContextMaxLength {
98+ return errCertificateRequestContextTooLong
99+ }
100+
101+ if len (out ) < m .Size () {
102+ return errBufferTooSmall
103+ }
104+
105+ // Check size of certificate_list is still within bounds
106+ if m .certsSize () > maxUint24 {
107+ return errCertificateListTooLong
108+ }
109+
74110 // Start with certificate_request_context (1-byte length prefix)
75111 //nolint:gosec // G115: certificate_request_context length is validated to be <= 255 above.
76- out := []byte {byte (len (m .CertificateRequestContext ))}
77- out = append (out , m .CertificateRequestContext ... )
112+ offset := 0
113+ out [0 ] = byte (len (m .CertificateRequestContext ))
114+ offset += 1
115+ n := copy (out [offset :], m .CertificateRequestContext )
116+ offset += n
117+
118+ // Add certificate_list with 3-byte length prefix
119+ util .PutBigEndianUint24 (out [offset :], uint32 (m .certsSize ())) //nolint:gosec // G115
120+ offset += 3
78121
79122 // Build certificate_list
80- certificateList := []byte {}
81123 for _ , entry := range m .CertificateList {
82124 // Add cert_data as a 3-byte length prefix
83125 certDataLen := len (entry .CertificateData )
84126 if certDataLen == 0 || certDataLen > maxUint24 {
85- return nil , errInvalidCertificateEntry
127+ return errInvalidCertificateEntry
86128 }
87- certDataLenBytes := make ([] byte , cert13CertLengthFieldSize )
88- util . PutBigEndianUint24 ( certDataLenBytes , uint32 ( certDataLen )) //nolint:gosec // G115
89- certificateList = append ( certificateList , certDataLenBytes ... )
90- certificateList = append ( certificateList , entry . CertificateData ... )
129+ util . PutBigEndianUint24 ( out [ offset :], uint32 ( certDataLen )) //nolint:gosec // G115
130+ offset += 3
131+ n = copy ( out [ offset :], entry . CertificateData )
132+ offset += n
91133
92134 // Marshal extensions (includes a 2-byte length prefix)
93135 extensionsData , err := extension .Marshal (entry .Extensions )
94136 if err != nil {
95- return nil , err
137+ return err
96138 }
97- certificateList = append (certificateList , extensionsData ... )
139+ n = copy (out [offset :], extensionsData )
140+ offset += n
98141
99- // Check size of certificate_list is still within bounds
100- if len (certificateList ) > maxUint24 {
101- return nil , errCertificateListTooLong
102- }
103142 }
104143
105- // Add certificate_list with 3-byte length prefix
106- certificateListLenBytes := make ([]byte , cert13CertLengthFieldSize )
107- util .PutBigEndianUint24 (certificateListLenBytes , uint32 (len (certificateList ))) //nolint:gosec // G115
108- out = append (out , certificateListLenBytes ... )
109- out = append (out , certificateList ... )
110-
111- return out , nil
144+ return nil
112145}
113146
114147// parseCertificate13Entry parses a single certificate entry from the cryptobyte string.
0 commit comments