Skip to content

Commit a171e83

Browse files
authored
Merge pull request #27 from b-open-io/opl-335-bytes
OPL-335 Byte types
2 parents 189b854 + fb8225c commit a171e83

File tree

75 files changed

+2001
-1629
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

75 files changed

+2001
-1629
lines changed

auth/certificates/certificate.go

Lines changed: 107 additions & 175 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,12 @@ 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"
13+
"github.com/bsv-blockchain/go-sdk/transaction"
1914
"github.com/bsv-blockchain/go-sdk/wallet"
15+
"github.com/bsv-blockchain/go-sdk/wallet/serializer"
2016
)
2117

2218
var (
@@ -29,10 +25,10 @@ var (
2925
// It provides methods for serialization, deserialization, signing, and verifying certificates.
3026
type Certificate struct {
3127
// Type identifier for the certificate, base64 encoded string, 32 bytes
32-
Type wallet.Base64String `json:"type"`
28+
Type wallet.StringBase64 `json:"type"`
3329

3430
// Unique serial number of the certificate, base64 encoded string, 32 bytes
35-
SerialNumber wallet.Base64String `json:"serialNumber"`
31+
SerialNumber wallet.StringBase64 `json:"serialNumber"`
3632

3733
// The public key belonging to the certificate's subject
3834
Subject ec.PublicKey `json:"subject"`
@@ -41,23 +37,23 @@ type Certificate struct {
4137
Certifier ec.PublicKey `json:"certifier"`
4238

4339
// The outpoint used to confirm that the certificate has not been revoked
44-
RevocationOutpoint *overlay.Outpoint `json:"revocationOutpoint"`
40+
RevocationOutpoint *transaction.Outpoint `json:"revocationOutpoint"`
4541

4642
// All the fields present in the certificate, with field names as keys and encrypted field values as strings
47-
Fields map[wallet.CertificateFieldNameUnder50Bytes]wallet.Base64String `json:"fields"`
43+
Fields map[wallet.CertificateFieldNameUnder50Bytes]wallet.StringBase64 `json:"fields"`
4844

4945
// Certificate signature by the certifier's private key
5046
Signature []byte `json:"signature,omitempty"`
5147
}
5248

5349
// NewCertificate creates a new certificate with the given fields
5450
func NewCertificate(
55-
certType wallet.Base64String,
56-
serialNumber wallet.Base64String,
51+
certType wallet.StringBase64,
52+
serialNumber wallet.StringBase64,
5753
subject ec.PublicKey,
5854
certifier ec.PublicKey,
59-
revocationOutpoint *overlay.Outpoint,
60-
fields map[wallet.CertificateFieldNameUnder50Bytes]wallet.Base64String,
55+
revocationOutpoint *transaction.Outpoint,
56+
fields map[wallet.CertificateFieldNameUnder50Bytes]wallet.StringBase64,
6157
signature []byte,
6258
) *Certificate {
6359
return &Certificate{
@@ -73,181 +69,37 @@ func NewCertificate(
7369

7470
// ToBinary serializes the certificate into binary format
7571
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 (Base64String, 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 (Base64String, 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)))
12176

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)
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
14591
func CertificateFromBinary(data []byte) (*Certificate, error) {
146-
reader := util.NewReader(data)
147-
148-
// Read type (32 bytes)
149-
typeBytes, err := reader.ReadBytes(32)
92+
walletCert, err := serializer.DeserializeCertificate(data)
15093
if err != nil {
151-
return nil, fmt.Errorf("failed to read type: %w", err)
94+
return nil, fmt.Errorf("failed to deserialize certificate: %w", err)
15295
}
153-
typeStr := base64.StdEncoding.EncodeToString(typeBytes)
15496

155-
// Read serialNumber (32 bytes)
156-
serialNumberBytes, err := reader.ReadBytes(32)
97+
cert, err := FromWalletCertificate(walletCert)
15798
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)
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)
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)
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.Base64String)
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.Base64String(string(fieldValueBytes))
232-
233-
fields[fieldName] = fieldValue
99+
return nil, fmt.Errorf("failed to convert wallet certificate to Certificate: %w", err)
234100
}
235101

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.Base64String(typeStr),
244-
SerialNumber: wallet.Base64String(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
@@ -271,7 +123,7 @@ func (c *Certificate) Verify(ctx context.Context) error {
271123
}
272124

273125
// Parse the signature
274-
sig, err := ec.ParseSignature(c.Signature)
126+
signature, err := ec.ParseSignature(c.Signature)
275127
if err != nil {
276128
return fmt.Errorf("failed to parse signature: %w", err)
277129
}
@@ -290,7 +142,7 @@ func (c *Certificate) Verify(ctx context.Context) error {
290142
},
291143
},
292144
Data: data,
293-
Signature: *sig,
145+
Signature: signature,
294146
}
295147

296148
verifyResult, err := verifier.VerifySignature(ctx, verifyArgs, "")
@@ -354,6 +206,86 @@ 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 Fields map from map[CertificateFieldNameUnder50Bytes]StringBase64 to map[string]string
223+
fields := make(map[string]string)
224+
for fieldName, fieldValue := range c.Fields {
225+
fields[string(fieldName)] = string(fieldValue)
226+
}
227+
228+
// Convert []byte signature to *ec.Signature
229+
var signature *ec.Signature
230+
if len(c.Signature) > 0 {
231+
if sig, err := ec.ParseSignature(c.Signature); err == nil {
232+
signature = sig
233+
}
234+
}
235+
236+
return &wallet.Certificate{
237+
Type: certType,
238+
SerialNumber: serialNumber,
239+
Subject: &c.Subject, // Convert value type to pointer
240+
Certifier: &c.Certifier, // Convert value type to pointer
241+
RevocationOutpoint: c.RevocationOutpoint,
242+
Fields: fields,
243+
Signature: signature,
244+
}, nil
245+
}
246+
247+
func FromWalletCertificate(walletCert *wallet.Certificate) (*Certificate, error) {
248+
if walletCert == nil {
249+
return nil, fmt.Errorf("wallet certificate cannot be nil")
250+
}
251+
252+
// Convert CertificateType [32]byte to StringBase64
253+
certType := wallet.StringBase64FromArray(walletCert.Type)
254+
255+
// Convert SerialNumber [32]byte to StringBase64
256+
serialNumber := wallet.StringBase64FromArray(walletCert.SerialNumber)
257+
258+
// Convert ec.PublicKey to ec.PublicKey
259+
var subject, certifier ec.PublicKey
260+
if walletCert.Subject != nil {
261+
subject = *walletCert.Subject
262+
}
263+
if walletCert.Certifier != nil {
264+
certifier = *walletCert.Certifier
265+
}
266+
267+
// Convert fields map from map[string]string to map[CertificateFieldNameUnder50Bytes]StringBase64
268+
fields := make(map[wallet.CertificateFieldNameUnder50Bytes]wallet.StringBase64)
269+
for fieldName, fieldValue := range walletCert.Fields {
270+
fields[wallet.CertificateFieldNameUnder50Bytes(fieldName)] = wallet.StringBase64(fieldValue)
271+
}
272+
273+
var signature []byte
274+
if walletCert.Signature != nil {
275+
signature = walletCert.Signature.Serialize()
276+
}
277+
278+
return &Certificate{
279+
Type: certType,
280+
SerialNumber: serialNumber,
281+
Subject: subject,
282+
Certifier: certifier,
283+
RevocationOutpoint: walletCert.RevocationOutpoint,
284+
Fields: fields,
285+
Signature: signature,
286+
}, nil
287+
}
288+
357289
// GetCertificateEncryptionDetails returns protocol ID and key ID for certificate field encryption
358290
// For master certificate creation, no serial number is provided because entropy is required
359291
// from both the client and the certifier. In this case, the keyID is simply the fieldName.

0 commit comments

Comments
 (0)