Skip to content

Commit 409b2da

Browse files
authored
Merge pull request #19 from armortal/feat/rsa-oaep-encryption
Feat/rsa oaep encryption
2 parents b4bba5d + b7fd7cd commit 409b2da

File tree

8 files changed

+167
-53
lines changed

8 files changed

+167
-53
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ This library is still in active development and all algorithms are not yet suppo
3030
| Algorithm | encrypt | decrypt | sign | verify | digest | generateKey | deriveKey | deriveBits | importKey | exportKey | wrapKey | unwrapKey |
3131
| :--: | :--: | :--: | :--: | :--: | :--: | :--: | :--: | :--: | :--: | :--: | :--: | :--: |
3232
| [HMAC](#hmac) |||:white_check_mark:|:white_check_mark:||:white_check_mark:|||:white_check_mark:|:white_check_mark:|||
33-
| [RSA-OAEP](#rsa-oaep) ||||||:white_check_mark:|||:white_check_mark:|:white_check_mark:|||
33+
| [RSA-OAEP](#rsa-oaep) |:white_check_mark:|:white_check_mark:||||:white_check_mark:|||:white_check_mark:|:white_check_mark:|||
3434
| [SHA](#sha) |||||:white_check_mark:||||||||
3535

3636
## Getting started

algorithms/hmac/hmac.go

+10-11
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import (
2323
"errors"
2424
"fmt"
2525
"hash"
26-
"io"
2726

2827
"github.com/armortal/webcrypto-go"
2928
"github.com/armortal/webcrypto-go/util"
@@ -34,6 +33,10 @@ var usages = []webcrypto.KeyUsage{
3433
webcrypto.Verify,
3534
}
3635

36+
func init() {
37+
webcrypto.RegisterAlgorithm("HMAC", func() webcrypto.SubtleCrypto { return &SubtleCrypto{} })
38+
}
39+
3740
type SubtleCrypto struct {
3841
webcrypto.SubtleCrypto
3942
Hash webcrypto.Algorithm
@@ -96,7 +99,7 @@ func (a *SubtleCrypto) Name() string {
9699
return "HMAC"
97100
}
98101

99-
func (a *SubtleCrypto) Decrypt(algorithm webcrypto.Algorithm, key webcrypto.CryptoKey, data io.Reader) (any, error) {
102+
func (a *SubtleCrypto) Decrypt(algorithm webcrypto.Algorithm, key webcrypto.CryptoKey, data []byte) ([]byte, error) {
100103
return nil, webcrypto.ErrMethodNotSupported()
101104
}
102105

@@ -108,11 +111,11 @@ func (a *SubtleCrypto) DeriveKey(algorithm webcrypto.Algorithm, baseKey webcrypt
108111
return nil, webcrypto.ErrMethodNotSupported()
109112
}
110113

111-
func (a *SubtleCrypto) Digest(algorithm webcrypto.Algorithm, data io.Reader) ([]byte, error) {
114+
func (a *SubtleCrypto) Digest(algorithm webcrypto.Algorithm, data []byte) ([]byte, error) {
112115
return nil, webcrypto.ErrMethodNotSupported()
113116
}
114117

115-
func (a *SubtleCrypto) Encrypt(algorithm webcrypto.Algorithm, key webcrypto.CryptoKey, data io.Reader) (any, error) {
118+
func (a *SubtleCrypto) Encrypt(algorithm webcrypto.Algorithm, key webcrypto.CryptoKey, data []byte) ([]byte, error) {
116119
return nil, webcrypto.ErrMethodNotSupported()
117120
}
118121

@@ -345,7 +348,7 @@ func importKeyFromRaw(keyData []byte, algorithm *Algorithm, extractable bool, ke
345348
}, nil
346349
}
347350

348-
func (a *SubtleCrypto) Sign(algorithm webcrypto.Algorithm, key webcrypto.CryptoKey, data io.Reader) ([]byte, error) {
351+
func (a *SubtleCrypto) Sign(algorithm webcrypto.Algorithm, key webcrypto.CryptoKey, data []byte) ([]byte, error) {
349352
if _, ok := algorithm.(*Algorithm); !ok {
350353
return nil, errors.New("webcrypto: algorithm must be *hmac.Algorithm")
351354
}
@@ -370,11 +373,7 @@ func (a *SubtleCrypto) Sign(algorithm webcrypto.Algorithm, key webcrypto.CryptoK
370373
}
371374

372375
h := hmac.New(hash, k.secret)
373-
b, err := io.ReadAll(data)
374-
if err != nil {
375-
return nil, err
376-
}
377-
h.Write(b)
376+
h.Write(data)
378377
return h.Sum(nil), nil
379378
}
380379

@@ -388,7 +387,7 @@ func (a *SubtleCrypto) UnwrapKey(format webcrypto.KeyFormat,
388387
return nil, webcrypto.ErrMethodNotSupported()
389388
}
390389

391-
func (a *SubtleCrypto) Verify(algorithm webcrypto.Algorithm, key webcrypto.CryptoKey, signature []byte, data io.Reader) (bool, error) {
390+
func (a *SubtleCrypto) Verify(algorithm webcrypto.Algorithm, key webcrypto.CryptoKey, signature []byte, data []byte) (bool, error) {
392391
act, err := a.Sign(algorithm, key, data)
393392
if err != nil {
394393
return false, err

algorithms/hmac/hmac_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ func TestSign(t *testing.T) {
146146
t.Fatal(err)
147147
}
148148

149-
sig, err := subtle.Sign(&Algorithm{}, key, bytes.NewReader([]byte(input)))
149+
sig, err := subtle.Sign(&Algorithm{}, key, []byte(input))
150150
if err != nil {
151151
t.Fatal(err)
152152
}
@@ -175,7 +175,7 @@ func TestVerify(t *testing.T) {
175175
if err != nil {
176176
t.Fatal(err)
177177
}
178-
ok, err := subtle.Verify(&Algorithm{}, key, sig, bytes.NewReader([]byte(input)))
178+
ok, err := subtle.Verify(&Algorithm{}, key, sig, []byte(input))
179179
if err != nil {
180180
t.Fatal(err)
181181
}

algorithms/rsa/rsa.go

+100-12
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,13 @@ package rsa
1919
import (
2020
"crypto/rand"
2121
"crypto/rsa"
22+
"crypto/sha1"
23+
"crypto/sha256"
24+
"crypto/sha512"
2225
"crypto/x509"
2326
"encoding/base64"
24-
"errors"
2527
"fmt"
26-
"io"
28+
"hash"
2729
"math/big"
2830

2931
"github.com/armortal/webcrypto-go"
@@ -123,7 +125,7 @@ func (c *CryptoKeyPair) PrivateKey() webcrypto.CryptoKey {
123125

124126
type CryptoKey struct {
125127
isPrivate bool
126-
pub rsa.PublicKey
128+
pub *rsa.PublicKey
127129
priv *rsa.PrivateKey
128130
alg *KeyAlgorithm
129131
ext bool
@@ -149,8 +151,39 @@ func (c *CryptoKey) Usages() []webcrypto.KeyUsage {
149151
return c.usages
150152
}
151153

152-
func (a *SubtleCrypto) Decrypt(algorithm webcrypto.Algorithm, key webcrypto.CryptoKey, data io.Reader) (any, error) {
153-
return nil, errors.New("unimplemented")
154+
func (a *SubtleCrypto) Decrypt(algorithm webcrypto.Algorithm, key webcrypto.CryptoKey, data []byte) ([]byte, error) {
155+
alg, err := getAlgorithm(algorithm)
156+
if err != nil {
157+
return nil, err
158+
}
159+
if alg.Name != rsaOaep {
160+
return nil, webcrypto.NewError(webcrypto.ErrNotSupportedError, "encrypt not supported")
161+
}
162+
163+
k, ok := key.(*CryptoKey)
164+
if !ok {
165+
return nil, webcrypto.NewError(webcrypto.ErrDataError, "key must be *rsa.CryptoKey")
166+
}
167+
168+
if !k.isPrivate {
169+
return nil, webcrypto.NewError(webcrypto.ErrInvalidAccessError, "key must be private")
170+
}
171+
172+
hash, err := getHash(k.alg.Hash)
173+
if err != nil {
174+
return nil, err
175+
}
176+
label := make([]byte, 0)
177+
if alg.OaepParams != nil {
178+
label = alg.OaepParams.Label
179+
}
180+
181+
msg, err := rsa.DecryptOAEP(hash, rand.Reader, k.priv, data, label)
182+
if err != nil {
183+
return nil, webcrypto.NewError(webcrypto.ErrOperationError, err.Error())
184+
}
185+
186+
return msg, nil
154187
}
155188

156189
func (a *SubtleCrypto) DeriveBits(algorithm webcrypto.Algorithm, baseKey webcrypto.CryptoKey, length uint64) ([]byte, error) {
@@ -161,12 +194,44 @@ func (a *SubtleCrypto) DeriveKey(algorithm webcrypto.Algorithm, baseKey webcrypt
161194
return nil, webcrypto.ErrMethodNotSupported()
162195
}
163196

164-
func (a *SubtleCrypto) Digest(algorithm webcrypto.Algorithm, data io.Reader) ([]byte, error) {
197+
func (a *SubtleCrypto) Digest(algorithm webcrypto.Algorithm, data []byte) ([]byte, error) {
165198
return nil, webcrypto.ErrMethodNotSupported()
166199
}
167200

168-
func (a *SubtleCrypto) Encrypt(algorithm webcrypto.Algorithm, key webcrypto.CryptoKey, data io.Reader) (any, error) {
169-
return nil, errors.New("unimplemented")
201+
func (a *SubtleCrypto) Encrypt(algorithm webcrypto.Algorithm, key webcrypto.CryptoKey, data []byte) ([]byte, error) {
202+
alg, err := getAlgorithm(algorithm)
203+
if err != nil {
204+
return nil, err
205+
}
206+
if alg.Name != rsaOaep {
207+
return nil, webcrypto.NewError(webcrypto.ErrNotSupportedError, "encrypt not supported")
208+
}
209+
210+
k, ok := key.(*CryptoKey)
211+
if !ok {
212+
return nil, webcrypto.NewError(webcrypto.ErrDataError, "key must be *rsa.CryptoKey")
213+
}
214+
215+
if k.isPrivate {
216+
return nil, webcrypto.NewError(webcrypto.ErrInvalidAccessError, "key must be public")
217+
}
218+
219+
hash, err := getHash(k.alg.Hash)
220+
if err != nil {
221+
return nil, err
222+
}
223+
224+
label := make([]byte, 0)
225+
if alg.OaepParams != nil {
226+
label = alg.OaepParams.Label
227+
}
228+
229+
b, err := rsa.EncryptOAEP(hash, rand.Reader, k.pub, data, label)
230+
if err != nil {
231+
return nil, webcrypto.NewError(webcrypto.ErrOperationError, err.Error())
232+
}
233+
234+
return b, nil
170235
}
171236

172237
func (a *SubtleCrypto) ExportKey(format webcrypto.KeyFormat, key webcrypto.CryptoKey) (any, error) {
@@ -292,7 +357,7 @@ func (a *SubtleCrypto) generateKeyOaep(algorithm *HashedKeyGenParams, extractabl
292357

293358
// Create the CryptoKey object for the public key
294359
pub := &CryptoKey{
295-
pub: key.PublicKey,
360+
pub: &key.PublicKey,
296361
alg: alg,
297362
ext: true,
298363
usages: util.UsageIntersection([]webcrypto.KeyUsage{webcrypto.Encrypt, webcrypto.WrapKey}, keyUsages),
@@ -477,7 +542,7 @@ func (a *SubtleCrypto) importKeyJwk(keyData *webcrypto.JsonWebKey, algorithm *Al
477542
pub.E = int(big.NewInt(0).SetBytes(e).Int64())
478543
ck.alg.ModulusLength = uint64(pub.N.BitLen())
479544
ck.alg.PublicExponent = *big.NewInt(int64(pub.E))
480-
ck.pub = pub
545+
ck.pub = &pub
481546

482547
// Extract private data if it exists
483548
if keyData.D != "" {
@@ -536,7 +601,7 @@ func (a *SubtleCrypto) importKeyJwk(keyData *webcrypto.JsonWebKey, algorithm *Al
536601
return ck, nil
537602
}
538603

539-
func (a *SubtleCrypto) Sign(algorithm webcrypto.Algorithm, key webcrypto.CryptoKey, data io.Reader) ([]byte, error) {
604+
func (a *SubtleCrypto) Sign(algorithm webcrypto.Algorithm, key webcrypto.CryptoKey, data []byte) ([]byte, error) {
540605
return nil, webcrypto.ErrMethodNotSupported()
541606
}
542607

@@ -550,10 +615,33 @@ func (a *SubtleCrypto) UnwrapKey(format webcrypto.KeyFormat,
550615
return nil, webcrypto.ErrMethodNotSupported()
551616
}
552617

553-
func (a *SubtleCrypto) Verify(algorithm webcrypto.Algorithm, key webcrypto.CryptoKey, signature []byte, data io.Reader) (bool, error) {
618+
func (a *SubtleCrypto) Verify(algorithm webcrypto.Algorithm, key webcrypto.CryptoKey, signature []byte, data []byte) (bool, error) {
554619
return false, webcrypto.ErrMethodNotSupported()
555620
}
556621

557622
func (a *SubtleCrypto) WrapKey(format webcrypto.KeyFormat, key webcrypto.CryptoKey, wrappingKey webcrypto.CryptoKey, wrapAlgorithm webcrypto.Algorithm) (any, error) {
558623
return nil, webcrypto.ErrMethodNotSupported()
559624
}
625+
626+
func getAlgorithm(algorithm webcrypto.Algorithm) (*Algorithm, error) {
627+
alg, ok := algorithm.(*Algorithm)
628+
if !ok {
629+
return nil, webcrypto.NewError(webcrypto.ErrDataError, "algorithm must be *rsa.Algorithm")
630+
}
631+
return alg, nil
632+
}
633+
634+
func getHash(hash string) (hash.Hash, error) {
635+
switch hash {
636+
case "SHA-1":
637+
return sha1.New(), nil
638+
case "SHA-256":
639+
return sha256.New(), nil
640+
case "SHA-384":
641+
return sha512.New384(), nil
642+
case "SHA-512":
643+
return sha512.New(), nil
644+
default:
645+
return nil, webcrypto.NewError(webcrypto.ErrNotSupportedError, fmt.Sprintf("hash %s not supported", hash))
646+
}
647+
}

algorithms/rsa/rsa_test.go

+36
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
package rsa
1616

1717
import (
18+
"bytes"
1819
"crypto/rsa"
1920
"crypto/x509"
2021
"encoding/base64"
@@ -24,6 +25,41 @@ import (
2425
"github.com/armortal/webcrypto-go"
2526
)
2627

28+
func TestEncryptDecrypt(t *testing.T) {
29+
subtle := &SubtleCrypto{}
30+
key, err := subtle.GenerateKey(&Algorithm{
31+
Name: "RSA-OAEP",
32+
HashedKeyGenParams: &HashedKeyGenParams{
33+
KeyGenParams: KeyGenParams{
34+
ModulusLength: 2048,
35+
PublicExponent: *big.NewInt(65537),
36+
},
37+
Hash: "SHA-256",
38+
},
39+
}, true, webcrypto.Decrypt, webcrypto.Encrypt)
40+
if err != nil {
41+
t.Fatal(err)
42+
}
43+
44+
msg := []byte("helloworld")
45+
b, err := subtle.Encrypt(&Algorithm{
46+
Name: "RSA-OAEP",
47+
}, key.(*CryptoKeyPair).PublicKey(), msg)
48+
if err != nil {
49+
t.Fatal(err)
50+
}
51+
52+
v, err := subtle.Decrypt(&Algorithm{
53+
Name: "RSA-OAEP",
54+
}, key.(*CryptoKeyPair).PrivateKey(), b)
55+
if err != nil {
56+
t.Fatal(err)
57+
}
58+
if !bytes.Equal(msg, v) {
59+
t.Fatal("msg mismatch")
60+
}
61+
}
62+
2763
func TestOaep_ExportKey(t *testing.T) {
2864
subtle := &SubtleCrypto{}
2965
key, err := subtle.GenerateKey(&Algorithm{

algorithms/sha/sha.go

+6-12
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import (
2121
"crypto/sha256"
2222
"crypto/sha512"
2323
"hash"
24-
"io"
2524

2625
"github.com/armortal/webcrypto-go"
2726
)
@@ -52,7 +51,7 @@ type subtleCrypto struct {
5251
name string
5352
}
5453

55-
func (a *subtleCrypto) Decrypt(algorithm webcrypto.Algorithm, key webcrypto.CryptoKey, data io.Reader) (any, error) {
54+
func (a *subtleCrypto) Decrypt(algorithm webcrypto.Algorithm, key webcrypto.CryptoKey, data []byte) ([]byte, error) {
5655
return nil, webcrypto.ErrMethodNotSupported()
5756
}
5857

@@ -64,7 +63,7 @@ func (a *subtleCrypto) DeriveKey(algorithm webcrypto.Algorithm, baseKey webcrypt
6463
return nil, webcrypto.ErrMethodNotSupported()
6564
}
6665

67-
func (a *subtleCrypto) Digest(algorithm webcrypto.Algorithm, data io.Reader) ([]byte, error) {
66+
func (a *subtleCrypto) Digest(algorithm webcrypto.Algorithm, data []byte) ([]byte, error) {
6867
alg, ok := algorithm.(*Algorithm)
6968
if !ok {
7069
return nil, webcrypto.NewError(webcrypto.ErrDataError, "algorithm is not *sha.Algorithm")
@@ -82,16 +81,11 @@ func (a *subtleCrypto) Digest(algorithm webcrypto.Algorithm, data io.Reader) ([]
8281
default:
8382
return nil, webcrypto.NewError(webcrypto.ErrNotSupportedError, "algorithm name is not a valid SHA algorithm")
8483
}
85-
// TODO should read x bytes at a time
86-
b, err := io.ReadAll(data)
87-
if err != nil {
88-
return nil, err
89-
}
90-
hash.Write(b)
84+
hash.Write(data)
9185
return hash.Sum(nil), nil
9286
}
9387

94-
func (a *subtleCrypto) Encrypt(algorithm webcrypto.Algorithm, key webcrypto.CryptoKey, data io.Reader) (any, error) {
88+
func (a *subtleCrypto) Encrypt(algorithm webcrypto.Algorithm, key webcrypto.CryptoKey, data []byte) ([]byte, error) {
9589
return nil, webcrypto.ErrMethodNotSupported()
9690
}
9791

@@ -107,7 +101,7 @@ func (a *subtleCrypto) ImportKey(format webcrypto.KeyFormat, keyData any, algori
107101
return nil, webcrypto.ErrMethodNotSupported()
108102
}
109103

110-
func (a *subtleCrypto) Sign(algorithm webcrypto.Algorithm, key webcrypto.CryptoKey, data io.Reader) ([]byte, error) {
104+
func (a *subtleCrypto) Sign(algorithm webcrypto.Algorithm, key webcrypto.CryptoKey, data []byte) ([]byte, error) {
111105
return nil, webcrypto.ErrMethodNotSupported()
112106
}
113107

@@ -121,7 +115,7 @@ func (a *subtleCrypto) UnwrapKey(format webcrypto.KeyFormat,
121115
return nil, webcrypto.ErrMethodNotSupported()
122116
}
123117

124-
func (a *subtleCrypto) Verify(algorithm webcrypto.Algorithm, key webcrypto.CryptoKey, signature []byte, data io.Reader) (bool, error) {
118+
func (a *subtleCrypto) Verify(algorithm webcrypto.Algorithm, key webcrypto.CryptoKey, signature []byte, data []byte) (bool, error) {
125119
return false, webcrypto.ErrMethodNotSupported()
126120
}
127121

0 commit comments

Comments
 (0)