-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpartial_key.go
154 lines (122 loc) · 3.29 KB
/
partial_key.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
143
144
145
146
147
148
149
150
151
152
153
154
package octokey
import (
"bytes"
"crypto/rand"
"crypto/rsa"
"errors"
"github.com/ConradIrwin/mrsa"
"github.com/octokey/octokey-go/buffer"
"io"
"math/big"
"strings"
)
// A PartialKey is a triple (E, N, D) where E is the public exponent,
// N is the modulus, and D is a part of the mRSA private key.
type PartialKey mrsa.PrivateKey
const (
HEADER = "-----BEGIN MRSA PRIVATE KEY-----"
FOOTER = "-----END MRSA PRIVATE KEY-----"
KEY_TYPE = "octokey-mrsa"
EXPONENT = 65537
BIT_LENGTH = 2048
)
var (
ErrPartialKeyFormat = errors.New("octokey/partial_key: invalid input")
ErrPartialKeyWrongExponent = errors.New("octokey/partial_key: invalid exponent")
)
// GeneratePartialKey generates two new PartialKeys that can be used
// together to perform mRSA operations.
func GeneratePartialKey() (*PartialKey, *PartialKey, error) {
k, err := rsa.GenerateKey(rand.Reader, BIT_LENGTH)
if err != nil {
return nil, nil, err
}
d1, d2, err := mrsa.SplitPrivateKey(k)
if err != nil {
return nil, nil, err
}
return (*PartialKey)(d1), (*PartialKey)(d2), nil
}
// NewPartialKey reads a PartialKey from its string representation
func NewPartialKey(text string) (*PartialKey, error) {
text = strings.TrimSpace(text)
if !strings.HasPrefix(text, HEADER) {
return nil, ErrPartialKeyFormat
}
text = strings.TrimPrefix(text, HEADER)
if !strings.HasSuffix(text, FOOTER) {
return nil, ErrPartialKeyFormat
}
text = strings.TrimSuffix(text, FOOTER)
split := strings.Split(text, "\n\n")
if len(split) > 2 {
return nil, ErrPartialKeyFormat
}
base64 := strings.TrimSpace(split[len(split)-1])
b := buffer.NewBuffer(base64)
t := b.ScanString()
e := b.ScanMPInt()
n := b.ScanMPInt()
d := b.ScanMPInt()
b.ScanEof()
if b.Error != nil {
return nil, b.Error
}
if t != KEY_TYPE {
return nil, ErrPartialKeyFormat
}
if e.Cmp(big.NewInt(EXPONENT)) != 0 {
return nil, ErrPartialKeyWrongExponent
}
if d.Cmp(n) > 0 {
println("Oops d")
return nil, ErrPartialKeyWrongExponent
}
k := new(PartialKey)
k.E = EXPONENT
k.N = n
k.D = d
return k, nil
}
// PartialDecrypt runs partial mRSA decryption on a number. You will need to finalize the
// signature once you have run Sign with all parts of the key.
func (k *PartialKey) PartialDecrypt(c *big.Int) (*big.Int, error) {
mrsaKey := mrsa.PrivateKey(*k)
return mrsaKey.PartialDecrypt(c)
}
// Format gives you the partial key in the canonical representation including
// ----BEGIN/END headers.
func (k *PartialKey) String() string {
b := new(buffer.Buffer)
k.WriteBuffer(b)
if b.Error != nil {
panic(errors.New("invalid partial key: " + b.Error.Error()))
}
return HEADER + "\n" + lineWrap(b.String(), 64) + FOOTER + "\n"
}
// lineWrap wraps text at a given width. Used for formatting base64 buffers.
func lineWrap(s string, w int) string {
r := strings.NewReader(s)
l := make([]byte, w)
b := bytes.NewBuffer(make([]byte, 0, len(s)*65/64))
for {
n, err := r.Read(l)
if err == io.EOF {
break
}
if err != nil {
// Would indicate a bug in StringReader
panic(err)
}
b.Write(l[:n])
b.WriteString("\n")
}
return string(b.Bytes())
}
// WriteBuffer writes the PartialKey to a buffer
func (k *PartialKey) WriteBuffer(b *buffer.Buffer) {
b.AddString(KEY_TYPE)
b.AddMPInt(big.NewInt(int64(k.E)))
b.AddMPInt(k.N)
b.AddMPInt(k.D)
}