@@ -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+
77+ return out , err
78+ }
79+
80+ func (m * MessageCertificate13 ) Size () int {
81+ return 1 + len (m .CertificateRequestContext ) + cert13CertLengthFieldSize + m .certsSize ()
82+ }
83+
84+ func (m * MessageCertificate13 ) certsSize () int {
85+ certificateListSize := 0
86+ for _ , entry := range m .CertificateList {
87+ certificateListSize += cert13CertLengthFieldSize
88+ certificateListSize += len (entry .CertificateData )
89+ certificateListSize += extension .Size (entry .Extensions )
90+ }
91+
92+ return certificateListSize
93+ }
94+
95+ // MarshalInto is same as Marshal but uses a pre-allocated buffer.
96+ func (m * MessageCertificate13 ) MarshalInto (out []byte ) error {
97+ // Validate certificate_request_context length
98+ if len (m .CertificateRequestContext ) > cert13ContextMaxLength {
99+ return errCertificateRequestContextTooLong
100+ }
101+
102+ if len (out ) < m .Size () {
103+ return errBufferTooSmall
104+ }
105+
106+ // Check size of certificate_list is still within bounds
107+ if m .certsSize () > maxUint24 {
108+ return errCertificateListTooLong
109+ }
110+
74111 // Start with certificate_request_context (1-byte length prefix)
75112 //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 ... )
113+ offset := 0
114+ out [0 ] = byte (len (m .CertificateRequestContext )) //nolint:gosec // G115
115+ offset += 1
116+ n := copy (out [offset :], m .CertificateRequestContext ) //nolint:gosec // G115
117+ offset += n
118+
119+ // Add certificate_list with 3-byte length prefix
120+ util .PutBigEndianUint24 (out [offset :], uint32 (m .certsSize ())) //nolint:gosec // G115
121+ offset += 3
78122
79123 // Build certificate_list
80- certificateList := []byte {}
81124 for _ , entry := range m .CertificateList {
82125 // Add cert_data as a 3-byte length prefix
83126 certDataLen := len (entry .CertificateData )
84127 if certDataLen == 0 || certDataLen > maxUint24 {
85- return nil , errInvalidCertificateEntry
128+ return errInvalidCertificateEntry
86129 }
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 ... )
130+ util . PutBigEndianUint24 ( out [ offset :], uint32 ( certDataLen )) //nolint:gosec // G115
131+ offset += 3
132+ n = copy ( out [ offset :], entry . CertificateData )
133+ offset += n
91134
92135 // Marshal extensions (includes a 2-byte length prefix)
93136 extensionsData , err := extension .Marshal (entry .Extensions )
94137 if err != nil {
95- return nil , err
96- }
97- certificateList = append (certificateList , extensionsData ... )
98-
99- // Check size of certificate_list is still within bounds
100- if len (certificateList ) > maxUint24 {
101- return nil , errCertificateListTooLong
138+ return err
102139 }
140+ n = copy (out [offset :], extensionsData )
141+ offset += n
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