-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #46 from microsoft/rc4
Add support for RC4
- Loading branch information
Showing
3 changed files
with
226 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
//go:build windows | ||
// +build windows | ||
|
||
package cng | ||
|
||
import ( | ||
"runtime" | ||
|
||
"github.com/microsoft/go-crypto-winnative/internal/bcrypt" | ||
"github.com/microsoft/go-crypto-winnative/internal/subtle" | ||
) | ||
|
||
// A RC4Cipher is an instance of RC4 using a particular key. | ||
type RC4Cipher struct { | ||
kh bcrypt.KEY_HANDLE | ||
} | ||
|
||
// NewRC4Cipher creates and returns a new Cipher. | ||
func NewRC4Cipher(key []byte) (*RC4Cipher, error) { | ||
kh, err := newCipherHandle(bcrypt.RC4_ALGORITHM, "", key) | ||
if err != nil { | ||
return nil, err | ||
} | ||
c := &RC4Cipher{kh: kh} | ||
runtime.SetFinalizer(c, (*RC4Cipher).finalize) | ||
return c, nil | ||
} | ||
|
||
func (c *RC4Cipher) finalize() { | ||
if c.kh != 0 { | ||
bcrypt.DestroyKey(c.kh) | ||
} | ||
} | ||
|
||
// Reset zeros the key data and makes the Cipher unusable. | ||
func (c *RC4Cipher) Reset() { | ||
bcrypt.DestroyKey(c.kh) | ||
c.kh = 0 | ||
} | ||
|
||
// XORKeyStream sets dst to the result of XORing src with the key stream. | ||
// Dst and src must overlap entirely or not at all. | ||
func (c *RC4Cipher) XORKeyStream(dst, src []byte) { | ||
if c.kh == 0 || len(src) == 0 { | ||
return | ||
} | ||
if subtle.InexactOverlap(dst[:len(src)], src) { | ||
panic("crypto/rc4: invalid buffer overlap") | ||
} | ||
var outLen uint32 | ||
if err := bcrypt.Encrypt(c.kh, src, nil, nil, dst, &outLen, 0); err != nil { | ||
panic("crypto/rc4: encryption failed: " + err.Error()) | ||
} | ||
if int(outLen) != len(src) { | ||
panic("crypto/rc4: src not fully XORed") | ||
} | ||
runtime.KeepAlive(c) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
//go:build windows | ||
// +build windows | ||
|
||
package cng_test | ||
|
||
import ( | ||
"bytes" | ||
"fmt" | ||
"testing" | ||
|
||
"github.com/microsoft/go-crypto-winnative/cng" | ||
) | ||
|
||
type rc4Test struct { | ||
key, keystream []byte | ||
} | ||
|
||
var golden = []rc4Test{ | ||
// Test vectors from the original cypherpunk posting of ARC4: | ||
// https://groups.google.com/group/sci.crypt/msg/10a300c9d21afca0?pli=1 | ||
{ | ||
[]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}, | ||
[]byte{0x74, 0x94, 0xc2, 0xe7, 0x10, 0x4b, 0x08, 0x79}, | ||
}, | ||
{ | ||
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, | ||
[]byte{0xde, 0x18, 0x89, 0x41, 0xa3, 0x37, 0x5d, 0x3a}, | ||
}, | ||
{ | ||
[]byte{0xef, 0x01, 0x23, 0x45}, | ||
[]byte{0xd6, 0xa1, 0x41, 0xa7, 0xec, 0x3c, 0x38, 0xdf, 0xbd, 0x61}, | ||
}, | ||
|
||
// Test vectors from the Wikipedia page: https://en.wikipedia.org/wiki/RC4 | ||
{ | ||
[]byte{0x4b, 0x65, 0x79}, | ||
[]byte{0xeb, 0x9f, 0x77, 0x81, 0xb7, 0x34, 0xca, 0x72, 0xa7, 0x19}, | ||
}, | ||
{ | ||
[]byte{0x57, 0x69, 0x6b, 0x69}, | ||
[]byte{0x60, 0x44, 0xdb, 0x6d, 0x41, 0xb7}, | ||
}, | ||
{ | ||
[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, | ||
[]byte{ | ||
0xde, 0x18, 0x89, 0x41, 0xa3, 0x37, 0x5d, 0x3a, | ||
0x8a, 0x06, 0x1e, 0x67, 0x57, 0x6e, 0x92, 0x6d, | ||
0xc7, 0x1a, 0x7f, 0xa3, 0xf0, 0xcc, 0xeb, 0x97, | ||
0x45, 0x2b, 0x4d, 0x32, 0x27, 0x96, 0x5f, 0x9e, | ||
0xa8, 0xcc, 0x75, 0x07, 0x6d, 0x9f, 0xb9, 0xc5, | ||
0x41, 0x7a, 0xa5, 0xcb, 0x30, 0xfc, 0x22, 0x19, | ||
0x8b, 0x34, 0x98, 0x2d, 0xbb, 0x62, 0x9e, 0xc0, | ||
0x4b, 0x4f, 0x8b, 0x05, 0xa0, 0x71, 0x08, 0x50, | ||
0x92, 0xa0, 0xc3, 0x58, 0x4a, 0x48, 0xe4, 0xa3, | ||
0x0a, 0x39, 0x7b, 0x8a, 0xcd, 0x1d, 0x00, 0x9e, | ||
0xc8, 0x7d, 0x68, 0x11, 0xf2, 0x2c, 0xf4, 0x9c, | ||
0xa3, 0xe5, 0x93, 0x54, 0xb9, 0x45, 0x15, 0x35, | ||
0xa2, 0x18, 0x7a, 0x86, 0x42, 0x6c, 0xca, 0x7d, | ||
0x5e, 0x82, 0x3e, 0xba, 0x00, 0x44, 0x12, 0x67, | ||
0x12, 0x57, 0xb8, 0xd8, 0x60, 0xae, 0x4c, 0xbd, | ||
0x4c, 0x49, 0x06, 0xbb, 0xc5, 0x35, 0xef, 0xe1, | ||
0x58, 0x7f, 0x08, 0xdb, 0x33, 0x95, 0x5c, 0xdb, | ||
0xcb, 0xad, 0x9b, 0x10, 0xf5, 0x3f, 0xc4, 0xe5, | ||
0x2c, 0x59, 0x15, 0x65, 0x51, 0x84, 0x87, 0xfe, | ||
0x08, 0x4d, 0x0e, 0x3f, 0x03, 0xde, 0xbc, 0xc9, | ||
0xda, 0x1c, 0xe9, 0x0d, 0x08, 0x5c, 0x2d, 0x8a, | ||
0x19, 0xd8, 0x37, 0x30, 0x86, 0x16, 0x36, 0x92, | ||
0x14, 0x2b, 0xd8, 0xfc, 0x5d, 0x7a, 0x73, 0x49, | ||
0x6a, 0x8e, 0x59, 0xee, 0x7e, 0xcf, 0x6b, 0x94, | ||
0x06, 0x63, 0xf4, 0xa6, 0xbe, 0xe6, 0x5b, 0xd2, | ||
0xc8, 0x5c, 0x46, 0x98, 0x6c, 0x1b, 0xef, 0x34, | ||
0x90, 0xd3, 0x7b, 0x38, 0xda, 0x85, 0xd3, 0x2e, | ||
0x97, 0x39, 0xcb, 0x23, 0x4a, 0x2b, 0xe7, 0x40, | ||
}, | ||
}, | ||
} | ||
|
||
func TestRC4Golden(t *testing.T) { | ||
for gi, g := range golden { | ||
data := make([]byte, len(g.keystream)) | ||
for i := range data { | ||
data[i] = byte(i) | ||
} | ||
|
||
expect := make([]byte, len(g.keystream)) | ||
for i := range expect { | ||
expect[i] = byte(i) ^ g.keystream[i] | ||
} | ||
|
||
for size := 1; size <= len(g.keystream); size++ { | ||
c, err := cng.NewRC4Cipher(g.key) | ||
if err != nil { | ||
t.Fatalf("#%d: NewCipher: %v", gi, err) | ||
} | ||
|
||
off := 0 | ||
for off < len(g.keystream) { | ||
n := len(g.keystream) - off | ||
if n > size { | ||
n = size | ||
} | ||
desc := fmt.Sprintf("#%d@[%d:%d]", gi, off, off+n) | ||
src := data[off : off+n] | ||
expect := expect[off : off+n] | ||
dst := make([]byte, len(src)) | ||
c.XORKeyStream(dst, src) | ||
for i, v := range dst { | ||
if v != expect[i] { | ||
t.Fatalf("%s: mismatch at byte %d:\nhave %x\nwant %x", desc, i, dst, expect) | ||
} | ||
} | ||
off += n | ||
} | ||
} | ||
} | ||
} | ||
|
||
func TestRC4Block(t *testing.T) { | ||
c1a, _ := cng.NewRC4Cipher(golden[0].key) | ||
c1b, _ := cng.NewRC4Cipher(golden[1].key) | ||
data1 := make([]byte, 1<<20) | ||
for i := range data1 { | ||
c1a.XORKeyStream(data1[i:i+1], data1[i:i+1]) | ||
c1b.XORKeyStream(data1[i:i+1], data1[i:i+1]) | ||
} | ||
|
||
c2a, _ := cng.NewRC4Cipher(golden[0].key) | ||
c2b, _ := cng.NewRC4Cipher(golden[1].key) | ||
data2 := make([]byte, 1<<20) | ||
c2a.XORKeyStream(data2, data2) | ||
c2b.XORKeyStream(data2, data2) | ||
|
||
if !bytes.Equal(data1, data2) { | ||
t.Fatalf("bad block") | ||
} | ||
} | ||
|
||
func benchmarkRC4(b *testing.B, size int64) { | ||
buf := make([]byte, size) | ||
c, err := cng.NewRC4Cipher(golden[0].key) | ||
if err != nil { | ||
panic(err) | ||
} | ||
b.SetBytes(size) | ||
|
||
for i := 0; i < b.N; i++ { | ||
c.XORKeyStream(buf, buf) | ||
} | ||
} | ||
|
||
func BenchmarkRC4_128(b *testing.B) { | ||
benchmarkRC4(b, 128) | ||
} | ||
|
||
func BenchmarkRC4_1K(b *testing.B) { | ||
benchmarkRC4(b, 1024) | ||
} | ||
|
||
func BenchmarkRC4_8K(b *testing.B) { | ||
benchmarkRC4(b, 8096) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters