@@ -7,16 +7,12 @@ package certificates
77
88import (
99 "context"
10- "encoding/base64"
1110 "errors"
1211 "fmt"
13- "sort"
14-
15- "github.com/bsv-blockchain/go-sdk/chainhash"
1612 "github.com/bsv-blockchain/go-sdk/overlay"
1713 ec "github.com/bsv-blockchain/go-sdk/primitives/ec"
18- "github.com/bsv-blockchain/go-sdk/util"
1914 "github.com/bsv-blockchain/go-sdk/wallet"
15+ "github.com/bsv-blockchain/go-sdk/wallet/serializer"
2016)
2117
2218var (
@@ -73,181 +69,37 @@ func NewCertificate(
7369
7470// ToBinary serializes the certificate into binary format
7571func (c * Certificate ) ToBinary (includeSignature bool ) ([]byte , error ) {
76- // ensure parameters are valid
77- if c .Type == "" || c .SerialNumber == "" || c .RevocationOutpoint == nil || c .Fields == nil {
78- return nil , ErrInvalidCertificate
79- }
80-
81- writer := util .NewWriter ()
82-
83- // Write type (StringBase64, 32 bytes)
84- typeBytes , err := base64 .StdEncoding .DecodeString (string (c .Type ))
85- if err != nil {
86- return nil , fmt .Errorf ("invalid type encoding: %w" , err )
87- }
88- writer .WriteBytes (typeBytes )
89-
90- // Write serialNumber (StringBase64, 32 bytes)
91- serialNumberBytes , err := base64 .StdEncoding .DecodeString (string (c .SerialNumber ))
72+ walletCert , err := c .ToWalletCertificate ()
9273 if err != nil {
93- return nil , fmt .Errorf ("invalid serial number encoding : %w" , err )
74+ return nil , fmt .Errorf ("failed to convert certificate to wallet format : %w" , err )
9475 }
95- writer .WriteBytes (serialNumberBytes )
96-
97- // Write subject (33 bytes compressed public key)
98- subjectBytes := c .Subject .Compressed ()
99- writer .WriteBytes (subjectBytes )
100-
101- // Write certifier (33 bytes compressed public key)
102- certifierBytes := c .Certifier .Compressed ()
103- writer .WriteBytes (certifierBytes )
104-
105- // Write revocationOutpoint (TXID + OutputIndex)
106- writer .WriteBytes (c .RevocationOutpoint .Txid [:])
107- writer .WriteVarInt (uint64 (c .RevocationOutpoint .OutputIndex ))
108-
109- // Write fields
110- // Sort field names lexicographically
111- fieldNames := make ([]wallet.CertificateFieldNameUnder50Bytes , 0 , len (c .Fields ))
112- for fieldName := range c .Fields {
113- fieldNames = append (fieldNames , fieldName )
114- }
115- sort .Slice (fieldNames , func (i , j int ) bool {
116- return fieldNames [i ] < fieldNames [j ]
117- })
118-
119- // Write field count as varint
120- writer .WriteVarInt (uint64 (len (fieldNames )))
121-
122- for _ , fieldName := range fieldNames {
123- fieldValue := c .Fields [fieldName ]
12476
125- // Field name length + name
126- fieldNameBytes := []byte (fieldName )
127- writer .WriteVarInt (uint64 (len (fieldNameBytes )))
128- writer .WriteBytes (fieldNameBytes )
129-
130- // Field value length + value
131- fieldValueBytes := []byte (fieldValue )
132- writer .WriteVarInt (uint64 (len (fieldValueBytes )))
133- writer .WriteBytes (fieldValueBytes )
77+ var data []byte
78+ if includeSignature {
79+ data , err = serializer .SerializeCertificate (walletCert )
80+ } else {
81+ data , err = serializer .SerializeCertificateNoSignature (walletCert )
13482 }
135-
136- // Write signature if included
137- if includeSignature && len (c .Signature ) > 0 {
138- writer .WriteBytes (c .Signature )
83+ if err != nil {
84+ return nil , fmt .Errorf ("failed to serialize certificate: %w" , err )
13985 }
14086
141- return writer . Buf , nil
87+ return data , nil
14288}
14389
14490// CertificateFromBinary deserializes a certificate from binary format
14591func CertificateFromBinary (data []byte ) (* Certificate , error ) {
146- reader := util .NewReader (data )
147-
148- // Read type (32 bytes)
149- typeBytes , err := reader .ReadBytes (32 )
150- if err != nil {
151- return nil , fmt .Errorf ("failed to read type: %w" , err )
152- }
153- typeStr := base64 .StdEncoding .EncodeToString (typeBytes )
154-
155- // Read serialNumber (32 bytes)
156- serialNumberBytes , err := reader .ReadBytes (32 )
157- if err != nil {
158- return nil , fmt .Errorf ("failed to read serial number: %w" , err )
159- }
160- serialNumber := base64 .StdEncoding .EncodeToString (serialNumberBytes )
161-
162- // Read subject (33 bytes)
163- subjectBytes , err := reader .ReadBytes (33 )
164- if err != nil {
165- return nil , fmt .Errorf ("failed to read subject: %w" , err )
166- }
167- subject , err := ec .ParsePubKey (subjectBytes )
92+ walletCert , err := serializer .DeserializeCertificate (data )
16893 if err != nil {
169- return nil , fmt .Errorf ("invalid subject public key : %w" , err )
94+ return nil , fmt .Errorf ("failed to deserialize certificate : %w" , err )
17095 }
17196
172- // Read certifier (33 bytes)
173- certifierBytes , err := reader .ReadBytes (33 )
97+ cert , err := FromWalletCertificate (walletCert )
17498 if err != nil {
175- return nil , fmt .Errorf ("failed to read certifier: %w" , err )
176- }
177- certifier , err := ec .ParsePubKey (certifierBytes )
178- if err != nil {
179- return nil , fmt .Errorf ("invalid certifier public key: %w" , err )
180- }
181-
182- // Read revocationOutpoint
183- txidBytes , err := reader .ReadBytes (32 )
184- if err != nil {
185- return nil , fmt .Errorf ("failed to read txid: %w" , err )
186- }
187- outputIndex , err := reader .ReadVarInt ()
188- if err != nil {
189- return nil , fmt .Errorf ("failed to read output index: %w" , err )
99+ return nil , fmt .Errorf ("failed to convert wallet certificate to Certificate: %w" , err )
190100 }
191101
192- // Create revocation outpoint
193- revocationOutpoint := & overlay.Outpoint {
194- Txid : chainhash .Hash (txidBytes ),
195- OutputIndex : uint32 (outputIndex ),
196- }
197-
198- // Read field count (varint)
199- fieldCount , err := reader .ReadVarInt ()
200- if err != nil {
201- return nil , fmt .Errorf ("failed to read field count: %w" , err )
202- }
203-
204- // Read fields
205- fields := make (map [wallet.CertificateFieldNameUnder50Bytes ]wallet.StringBase64 )
206- for i := uint64 (0 ); i < fieldCount ; i ++ {
207- // Field name length (varint)
208- fieldNameLength , err := reader .ReadVarInt ()
209- if err != nil {
210- return nil , fmt .Errorf ("failed to read field name length: %w" , err )
211- }
212-
213- // Field name
214- fieldNameBytes , err := reader .ReadBytes (int (fieldNameLength ))
215- if err != nil {
216- return nil , fmt .Errorf ("failed to read field name: %w" , err )
217- }
218- fieldName := wallet .CertificateFieldNameUnder50Bytes (string (fieldNameBytes ))
219-
220- // Field value length (varint)
221- fieldValueLength , err := reader .ReadVarInt ()
222- if err != nil {
223- return nil , fmt .Errorf ("failed to read field value length: %w" , err )
224- }
225-
226- // Field value
227- fieldValueBytes , err := reader .ReadBytes (int (fieldValueLength ))
228- if err != nil {
229- return nil , fmt .Errorf ("failed to read field value: %w" , err )
230- }
231- fieldValue := wallet .StringBase64 (string (fieldValueBytes ))
232-
233- fields [fieldName ] = fieldValue
234- }
235-
236- // Read signature if present
237- var signature []byte
238- if reader .Pos < len (data ) {
239- signature = reader .ReadRemaining ()
240- }
241-
242- return & Certificate {
243- Type : wallet .StringBase64 (typeStr ),
244- SerialNumber : wallet .StringBase64 (serialNumber ),
245- Subject : * subject ,
246- Certifier : * certifier ,
247- RevocationOutpoint : revocationOutpoint ,
248- Fields : fields ,
249- Signature : signature ,
250- }, nil
102+ return cert , nil
251103}
252104
253105// Verify checks the certificate's validity including signature verification
@@ -354,6 +206,104 @@ func (c *Certificate) Sign(ctx context.Context, certifierWallet *wallet.ProtoWal
354206 return nil
355207}
356208
209+ func (c * Certificate ) ToWalletCertificate () (* wallet.Certificate , error ) {
210+ // Convert StringBase64 type to CertificateType [32]byte
211+ certType , err := c .Type .ToArray ()
212+ if err != nil {
213+ return nil , fmt .Errorf ("invalid certificate type: %w" , err )
214+ }
215+
216+ // Convert StringBase64 serial number to SerialNumber [32]byte
217+ serialNumber , err := c .SerialNumber .ToArray ()
218+ if err != nil {
219+ return nil , fmt .Errorf ("invalid serial number: %w" , err )
220+ }
221+
222+ // Convert overlay.Outpoint to wallet.Outpoint
223+ var revocationOutpoint * wallet.Outpoint
224+ if c .RevocationOutpoint != nil {
225+ revocationOutpoint = & wallet.Outpoint {
226+ Txid : c .RevocationOutpoint .Txid ,
227+ Index : c .RevocationOutpoint .OutputIndex ,
228+ }
229+ }
230+
231+ // Convert Fields map from map[CertificateFieldNameUnder50Bytes]StringBase64 to map[string]string
232+ fields := make (map [string ]string )
233+ for fieldName , fieldValue := range c .Fields {
234+ fields [string (fieldName )] = string (fieldValue )
235+ }
236+
237+ // Convert []byte signature to *ec.Signature
238+ var signature * ec.Signature
239+ if len (c .Signature ) > 0 {
240+ if sig , err := ec .ParseSignature (c .Signature ); err == nil {
241+ signature = sig
242+ }
243+ }
244+
245+ return & wallet.Certificate {
246+ Type : certType ,
247+ SerialNumber : serialNumber ,
248+ Subject : & c .Subject , // Convert value type to pointer
249+ Certifier : & c .Certifier , // Convert value type to pointer
250+ RevocationOutpoint : revocationOutpoint ,
251+ Fields : fields ,
252+ Signature : signature ,
253+ }, nil
254+ }
255+
256+ func FromWalletCertificate (walletCert * wallet.Certificate ) (* Certificate , error ) {
257+ if walletCert == nil {
258+ return nil , fmt .Errorf ("wallet certificate cannot be nil" )
259+ }
260+
261+ // Convert CertificateType [32]byte to StringBase64
262+ certType := wallet .StringBase64FromArray (walletCert .Type )
263+
264+ // Convert SerialNumber [32]byte to StringBase64
265+ serialNumber := wallet .StringBase64FromArray (walletCert .SerialNumber )
266+
267+ // Convert ec.PublicKey to ec.PublicKey
268+ var subject , certifier ec.PublicKey
269+ if walletCert .Subject != nil {
270+ subject = * walletCert .Subject
271+ }
272+ if walletCert .Certifier != nil {
273+ certifier = * walletCert .Certifier
274+ }
275+
276+ // Convert wallet.Outpoint to overlay.Outpoint
277+ var revocationOutpoint * overlay.Outpoint
278+ if walletCert .RevocationOutpoint != nil {
279+ revocationOutpoint = & overlay.Outpoint {
280+ Txid : walletCert .RevocationOutpoint .Txid ,
281+ OutputIndex : walletCert .RevocationOutpoint .Index ,
282+ }
283+ }
284+
285+ // Convert fields map from map[string]string to map[CertificateFieldNameUnder50Bytes]StringBase64
286+ fields := make (map [wallet.CertificateFieldNameUnder50Bytes ]wallet.StringBase64 )
287+ for fieldName , fieldValue := range walletCert .Fields {
288+ fields [wallet .CertificateFieldNameUnder50Bytes (fieldName )] = wallet .StringBase64 (fieldValue )
289+ }
290+
291+ var signature []byte
292+ if walletCert .Signature != nil {
293+ signature = walletCert .Signature .Serialize ()
294+ }
295+
296+ return & Certificate {
297+ Type : certType ,
298+ SerialNumber : serialNumber ,
299+ Subject : subject ,
300+ Certifier : certifier ,
301+ RevocationOutpoint : revocationOutpoint ,
302+ Fields : fields ,
303+ Signature : signature ,
304+ }, nil
305+ }
306+
357307// GetCertificateEncryptionDetails returns protocol ID and key ID for certificate field encryption
358308// For master certificate creation, no serial number is provided because entropy is required
359309// from both the client and the certifier. In this case, the keyID is simply the fieldName.
0 commit comments