Skip to content

Commit 7271ed0

Browse files
authored
Merge pull request #28 from b-open-io/opl-363-combine-types
OPL-363 Combine types
2 parents 53e9120 + 1692121 commit 7271ed0

27 files changed

+431
-471
lines changed

auth/certificates/certificate.go

Lines changed: 98 additions & 167 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,11 @@ package certificates
77

88
import (
99
"context"
10-
"encoding/base64"
1110
"errors"
1211
"fmt"
13-
"sort"
14-
15-
"github.com/bsv-blockchain/go-sdk/chainhash"
16-
"github.com/bsv-blockchain/go-sdk/overlay"
1712
ec "github.com/bsv-blockchain/go-sdk/primitives/ec"
18-
"github.com/bsv-blockchain/go-sdk/util"
1913
"github.com/bsv-blockchain/go-sdk/wallet"
14+
"github.com/bsv-blockchain/go-sdk/wallet/serializer"
2015
)
2116

2217
var (
@@ -41,7 +36,7 @@ type Certificate struct {
4136
Certifier ec.PublicKey `json:"certifier"`
4237

4338
// The outpoint used to confirm that the certificate has not been revoked
44-
RevocationOutpoint *overlay.Outpoint `json:"revocationOutpoint"`
39+
RevocationOutpoint *wallet.Outpoint `json:"revocationOutpoint"`
4540

4641
// All the fields present in the certificate, with field names as keys and encrypted field values as strings
4742
Fields map[wallet.CertificateFieldNameUnder50Bytes]wallet.StringBase64 `json:"fields"`
@@ -56,7 +51,7 @@ func NewCertificate(
5651
serialNumber wallet.StringBase64,
5752
subject ec.PublicKey,
5853
certifier ec.PublicKey,
59-
revocationOutpoint *overlay.Outpoint,
54+
revocationOutpoint *wallet.Outpoint,
6055
fields map[wallet.CertificateFieldNameUnder50Bytes]wallet.StringBase64,
6156
signature []byte,
6257
) *Certificate {
@@ -73,181 +68,37 @@ func NewCertificate(
7368

7469
// ToBinary serializes the certificate into binary format
7570
func (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))
71+
walletCert, err := c.ToWalletCertificate()
9272
if err != nil {
93-
return nil, fmt.Errorf("invalid serial number encoding: %w", err)
73+
return nil, fmt.Errorf("failed to convert certificate to wallet format: %w", err)
9474
}
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)))
12175

122-
for _, fieldName := range fieldNames {
123-
fieldValue := c.Fields[fieldName]
124-
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)
76+
var data []byte
77+
if includeSignature {
78+
data, err = serializer.SerializeCertificate(walletCert)
79+
} else {
80+
data, err = serializer.SerializeCertificateNoSignature(walletCert)
13481
}
135-
136-
// Write signature if included
137-
if includeSignature && len(c.Signature) > 0 {
138-
writer.WriteBytes(c.Signature)
82+
if err != nil {
83+
return nil, fmt.Errorf("failed to serialize certificate: %w", err)
13984
}
14085

141-
return writer.Buf, nil
86+
return data, nil
14287
}
14388

14489
// CertificateFromBinary deserializes a certificate from binary format
14590
func 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)
91+
walletCert, err := serializer.DeserializeCertificate(data)
16492
if err != nil {
165-
return nil, fmt.Errorf("failed to read subject: %w", err)
166-
}
167-
subject, err := ec.ParsePubKey(subjectBytes)
168-
if err != nil {
169-
return nil, fmt.Errorf("invalid subject public key: %w", err)
170-
}
171-
172-
// Read certifier (33 bytes)
173-
certifierBytes, err := reader.ReadBytes(33)
174-
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)
93+
return nil, fmt.Errorf("failed to deserialize certificate: %w", err)
18094
}
18195

182-
// Read revocationOutpoint
183-
txidBytes, err := reader.ReadBytes(32)
96+
cert, err := FromWalletCertificate(walletCert)
18497
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)
190-
}
191-
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
98+
return nil, fmt.Errorf("failed to convert wallet certificate to Certificate: %w", err)
23499
}
235100

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
101+
return cert, nil
251102
}
252103

253104
// Verify checks the certificate's validity including signature verification
@@ -354,6 +205,86 @@ func (c *Certificate) Sign(ctx context.Context, certifierWallet *wallet.ProtoWal
354205
return nil
355206
}
356207

208+
func (c *Certificate) ToWalletCertificate() (*wallet.Certificate, error) {
209+
// Convert StringBase64 type to CertificateType [32]byte
210+
certType, err := c.Type.ToArray()
211+
if err != nil {
212+
return nil, fmt.Errorf("invalid certificate type: %w", err)
213+
}
214+
215+
// Convert StringBase64 serial number to SerialNumber [32]byte
216+
serialNumber, err := c.SerialNumber.ToArray()
217+
if err != nil {
218+
return nil, fmt.Errorf("invalid serial number: %w", err)
219+
}
220+
221+
// Convert Fields map from map[CertificateFieldNameUnder50Bytes]StringBase64 to map[string]string
222+
fields := make(map[string]string)
223+
for fieldName, fieldValue := range c.Fields {
224+
fields[string(fieldName)] = string(fieldValue)
225+
}
226+
227+
// Convert []byte signature to *ec.Signature
228+
var signature *ec.Signature
229+
if len(c.Signature) > 0 {
230+
if sig, err := ec.ParseSignature(c.Signature); err == nil {
231+
signature = sig
232+
}
233+
}
234+
235+
return &wallet.Certificate{
236+
Type: certType,
237+
SerialNumber: serialNumber,
238+
Subject: &c.Subject, // Convert value type to pointer
239+
Certifier: &c.Certifier, // Convert value type to pointer
240+
RevocationOutpoint: c.RevocationOutpoint,
241+
Fields: fields,
242+
Signature: signature,
243+
}, nil
244+
}
245+
246+
func FromWalletCertificate(walletCert *wallet.Certificate) (*Certificate, error) {
247+
if walletCert == nil {
248+
return nil, fmt.Errorf("wallet certificate cannot be nil")
249+
}
250+
251+
// Convert CertificateType [32]byte to StringBase64
252+
certType := wallet.StringBase64FromArray(walletCert.Type)
253+
254+
// Convert SerialNumber [32]byte to StringBase64
255+
serialNumber := wallet.StringBase64FromArray(walletCert.SerialNumber)
256+
257+
// Convert ec.PublicKey to ec.PublicKey
258+
var subject, certifier ec.PublicKey
259+
if walletCert.Subject != nil {
260+
subject = *walletCert.Subject
261+
}
262+
if walletCert.Certifier != nil {
263+
certifier = *walletCert.Certifier
264+
}
265+
266+
// Convert fields map from map[string]string to map[CertificateFieldNameUnder50Bytes]StringBase64
267+
fields := make(map[wallet.CertificateFieldNameUnder50Bytes]wallet.StringBase64)
268+
for fieldName, fieldValue := range walletCert.Fields {
269+
fields[wallet.CertificateFieldNameUnder50Bytes(fieldName)] = wallet.StringBase64(fieldValue)
270+
}
271+
272+
var signature []byte
273+
if walletCert.Signature != nil {
274+
signature = walletCert.Signature.Serialize()
275+
}
276+
277+
return &Certificate{
278+
Type: certType,
279+
SerialNumber: serialNumber,
280+
Subject: subject,
281+
Certifier: certifier,
282+
RevocationOutpoint: walletCert.RevocationOutpoint,
283+
Fields: fields,
284+
Signature: signature,
285+
}, nil
286+
}
287+
357288
// GetCertificateEncryptionDetails returns protocol ID and key ID for certificate field encryption
358289
// For master certificate creation, no serial number is provided because entropy is required
359290
// from both the client and the certifier. In this case, the keyID is simply the fieldName.

0 commit comments

Comments
 (0)