-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcryptostring.go
142 lines (114 loc) · 3.54 KB
/
cryptostring.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
package goeznacl
import (
"errors"
"regexp"
"strings"
"github.com/darkwyrm/b85"
)
// This module contains the Go implementation of CryptoString, similar to the original
// implementation in originally written for PyMensago, but also includes some special sauce for
// better interaction with Go's libsodium API.
// CryptoString is a compact way of handling cryptographic hashes, signatures, and keys such that
// (1) the algorithm used is obvious and (2) the data is encoded as text. The RFC 1924 variant of
// Base85 encoding is used because it is more compact than Base64 and friendly to source code.
// The format looks like this: ALGORITHM:xxxxxxxxxxxxxxxxxxxx, where ALGORITHM is the name of the
// algorithm and the Xs represent the Base85-encoded data. The prefix is limited to 24 characters
// including the colon separator. Only capital ASCII letters, numbers, and dashes may be used in
// the prefix.
//
// Examples:
// - BLAKE3-256:^zPiV;CKvLd2(uwpIzmyMotYFsKM=cgbL=nSI2LN
// - ED25519:6lXjej0C~!F&_`qnkPHrC`z8+>;#g*fNfjV@4ngGlp#xsr8}1rS2(NG
//
var reFormatPattern = regexp.MustCompile("^[A-Z0-9-]{1,24}")
var ErrInvalidCS = errors.New("invalid cryptostring")
type CryptoString struct {
Prefix string
Data string
}
// New is just syntactic sugar for generating a quickie CryptoString from a string
func NewCS(str string) CryptoString {
var out CryptoString
out.Set(str)
return out
}
// NewFromBytes creates a CryptoString object from an algorithm and buffer of data. The new
// instance makes a copy of the data buffer passed to it
func NewCSFromBytes(algorithm string, buffer []byte) CryptoString {
var out CryptoString
out.SetFromBytes(algorithm, buffer)
return out
}
// Set takes a CryptoString-formatted string and sets the object to it.
func (cs *CryptoString) Set(str string) error {
cs.Prefix = ""
cs.Data = ""
// Data checks
if !reFormatPattern.MatchString(str) {
return ErrUnsupportedAlgorithm
}
parts := strings.SplitN(str, ":", 2)
if len(parts) != 2 || len(parts[1]) < 1 {
return ErrInvalidCS
}
_, err := b85.Decode(parts[1])
if err != nil {
return b85.ErrDecodingB85
}
cs.Prefix = parts[0]
cs.Data = parts[1]
return nil
}
// SetFromBytes assigns an algorithm and the associated data to the object. The caller retains
// ownership of the underlying data passed to it.
func (cs *CryptoString) SetFromBytes(algorithm string, buffer []byte) error {
if len(algorithm) > 0 {
if !reFormatPattern.MatchString(algorithm) {
return ErrInvalidCS
}
cs.Prefix = algorithm
} else {
cs.Prefix = ""
}
if buffer != nil {
cs.Data = b85.Encode(buffer)
} else {
cs.Data = ""
}
return nil
}
// AsString returns the state of the object as a CryptoString-formatted string
func (cs *CryptoString) AsString() string {
return cs.Prefix + ":" + cs.Data
}
// RawData returns the data of the object as a series of bytes. In the event of an error, nil is
// returned
func (cs *CryptoString) RawData() []byte {
out, err := b85.Decode(cs.Data)
if err != nil {
return nil
}
return out
}
// AsBytes returns the CryptoString as a byte array
func (cs *CryptoString) AsBytes() []byte {
return []byte(cs.Prefix + ":" + cs.Data)
}
// MakeEmpty returns the object to an uninitialized state
func (cs *CryptoString) MakeEmpty() {
cs.Prefix = ""
cs.Data = ""
}
// IsValid checks the internal data and returns True if it is valid
func (cs *CryptoString) IsValid() bool {
if !reFormatPattern.MatchString(cs.Prefix) {
return false
}
if len(cs.Data) < 1 {
return false
}
if _, err := b85.Decode(cs.Data); err != nil {
return false
}
return true
}