Resync
This commit is contained in:
100
go/simple-cipher/README.md
Normal file
100
go/simple-cipher/README.md
Normal file
@@ -0,0 +1,100 @@
|
||||
# Simple Cipher
|
||||
|
||||
Implement a simple shift cipher like Caesar and a more secure substitution cipher
|
||||
|
||||
## Step 1
|
||||
|
||||
"If he had anything confidential to say, he wrote it in cipher, that is,
|
||||
by so changing the order of the letters of the alphabet, that not a word
|
||||
could be made out. If anyone wishes to decipher these, and get at their
|
||||
meaning, he must substitute the fourth letter of the alphabet, namely D,
|
||||
for A, and so with the others."
|
||||
—Suetonius, Life of Julius Caesar
|
||||
|
||||
Ciphers are very straight-forward algorithms that allow us to render
|
||||
text less readable while still allowing easy deciphering. They are
|
||||
vulnerable to many forms of cryptoanalysis, but we are lucky that
|
||||
generally our little sisters are not cryptoanalysts.
|
||||
|
||||
The Caesar Cipher was used for some messages from Julius Caesar that
|
||||
were sent afield. Now Caesar knew that the cipher wasn't very good, but
|
||||
he had one ally in that respect: almost nobody could read well. So even
|
||||
being a couple letters off was sufficient so that people couldn't
|
||||
recognize the few words that they did know.
|
||||
|
||||
Your task is to create a simple shift cipher like the Caesar Cipher.
|
||||
This image is a great example of the Caesar Cipher: ![Caesar Cipher][1]
|
||||
|
||||
For example:
|
||||
|
||||
Giving "iamapandabear" as input to the encode function returns the cipher "ldpdsdqgdehdu". Obscure enough to keep our message secret in transit.
|
||||
|
||||
When "ldpdsdqgdehdu" is put into the decode function it would return
|
||||
the original "iamapandabear" letting your friend read your original
|
||||
message.
|
||||
|
||||
## Step 2
|
||||
|
||||
Shift ciphers are no fun though when your kid sister figures it out. Try
|
||||
amending the code to allow us to specify a key and use that for the
|
||||
shift distance. This is called a substitution cipher.
|
||||
|
||||
Here's an example:
|
||||
|
||||
Given the key "aaaaaaaaaaaaaaaaaa", encoding the string "iamapandabear"
|
||||
would return the original "iamapandabear".
|
||||
|
||||
Given the key "ddddddddddddddddd", encoding our string "iamapandabear"
|
||||
would return the obscured "lpdsdqgdehdu"
|
||||
|
||||
In the example above, we've set a = 0 for the key value. So when the
|
||||
plaintext is added to the key, we end up with the same message coming
|
||||
out. So "aaaa" is not an ideal key. But if we set the key to "dddd", we
|
||||
would get the same thing as the Caesar Cipher.
|
||||
|
||||
## Step 3
|
||||
|
||||
The weakest link in any cipher is the human being. Let's make your
|
||||
substitution cipher a little more fault tolerant by providing a source
|
||||
of randomness and ensuring that they key is not composed of numbers or
|
||||
capital letters.
|
||||
|
||||
If someone doesn't submit a key at all, generate a truly random key of
|
||||
at least 100 characters in length, accessible via Cipher#key (the #
|
||||
syntax means instance variable)
|
||||
|
||||
If the key submitted has capital letters or numbers, throw an
|
||||
ArgumentError with a message to that effect.
|
||||
|
||||
## Extensions
|
||||
|
||||
Shift ciphers work by making the text slightly odd, but are vulnerable
|
||||
to frequency analysis. Substitution ciphers help that, but are still
|
||||
very vulnerable when the key is short or if spaces are preserved. Later
|
||||
on you'll see one solution to this problem in the exercise
|
||||
"crypto-square".
|
||||
|
||||
If you want to go farther in this field, the questions begin to be about
|
||||
how we can exchange keys in a secure way. Take a look at [Diffie-Hellman
|
||||
on Wikipedia][dh] for one of the first implementations of this scheme.
|
||||
|
||||
[1]: http://upload.wikimedia.org/wikipedia/en/7/75/Caesar3.png
|
||||
[dh]: http://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange
|
||||
|
||||
To run the tests simply run the command `go test` in the exercise directory.
|
||||
|
||||
If the test suite contains benchmarks, you can run these with the `-bench`
|
||||
flag:
|
||||
|
||||
go test -bench .
|
||||
|
||||
For more detailed info about the Go track see the [help
|
||||
page](http://exercism.io/languages/go).
|
||||
|
||||
## Source
|
||||
|
||||
Substitution Cipher at Wikipedia [http://en.wikipedia.org/wiki/Substitution_cipher](http://en.wikipedia.org/wiki/Substitution_cipher)
|
||||
|
||||
## Submitting Incomplete Problems
|
||||
It's possible to submit an incomplete solution so you can see how others have completed the exercise.
|
||||
|
143
go/simple-cipher/cipher.go
Normal file
143
go/simple-cipher/cipher.go
Normal file
@@ -0,0 +1,143 @@
|
||||
package cipher
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Cipher is an interface for all ciphers
|
||||
type Cipher interface {
|
||||
Encode(string) string
|
||||
Decode(string) string
|
||||
}
|
||||
|
||||
// ShiftCipher is a cipher that shifts all letters a fixed distance
|
||||
type ShiftCipher struct {
|
||||
width int
|
||||
}
|
||||
|
||||
// NewShift returns a new ShiftCipher
|
||||
func NewShift(w int) *ShiftCipher {
|
||||
if w == 0 || w < -25 || w > 25 {
|
||||
return nil
|
||||
}
|
||||
c := new(ShiftCipher)
|
||||
c.width = w
|
||||
return c
|
||||
}
|
||||
|
||||
// Encode encodes a string using the current ShiftCipher
|
||||
func (c *ShiftCipher) Encode(s string) string {
|
||||
var ret string
|
||||
s = strings.ToLower(s)
|
||||
re := regexp.MustCompile("[^a-z]")
|
||||
s = re.ReplaceAllString(s, "")
|
||||
for i := range s {
|
||||
newChar := s[i] + byte(c.width)
|
||||
if newChar < 'a' {
|
||||
newChar = 'z' - ('a' - newChar) + 1
|
||||
}
|
||||
if newChar > 'z' {
|
||||
newChar = 'a' + (newChar - 'z') - 1
|
||||
}
|
||||
ret = ret + string(newChar)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// Decode decodes a string using the current ShiftCipher
|
||||
func (c *ShiftCipher) Decode(s string) string {
|
||||
var ret string
|
||||
s = strings.ToLower(s)
|
||||
re := regexp.MustCompile("[^a-z]")
|
||||
s = re.ReplaceAllString(s, "")
|
||||
for i := range s {
|
||||
newChar := s[i] - byte(c.width)
|
||||
if newChar < 'a' {
|
||||
newChar = 'z' - ('a' - newChar) + 1
|
||||
}
|
||||
if newChar > 'z' {
|
||||
newChar = 'a' + (newChar - 'z') - 1
|
||||
}
|
||||
ret = ret + string(newChar)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// A CeasarCipher is a ShiftCipher, just hardcoded at 3
|
||||
type CeasarCipher struct {
|
||||
c *ShiftCipher
|
||||
}
|
||||
|
||||
// NewCaesar creates a new CaesarCipher
|
||||
func NewCaesar() *CeasarCipher {
|
||||
c := new(CeasarCipher)
|
||||
c.c = NewShift(3)
|
||||
return c
|
||||
}
|
||||
|
||||
// Encode just calls the shift cipher in the caesar cipher
|
||||
func (c *CeasarCipher) Encode(s string) string {
|
||||
return c.c.Encode(s)
|
||||
}
|
||||
|
||||
// Decode just calls the shift cipher in the caesar cipher
|
||||
func (c *CeasarCipher) Decode(s string) string {
|
||||
return c.c.Decode(s)
|
||||
}
|
||||
|
||||
// VigenereCipher a cipher where each character can have a different shift width
|
||||
type VigenereCipher struct {
|
||||
key string
|
||||
}
|
||||
|
||||
// Encode encodes the given string
|
||||
func (c *VigenereCipher) Encode(s string) string {
|
||||
var ret string
|
||||
s = strings.ToLower(s)
|
||||
re := regexp.MustCompile("[^a-z]")
|
||||
s = re.ReplaceAllString(s, "")
|
||||
for i := range s {
|
||||
keyChar := c.key[i%len(c.key)] - 'a'
|
||||
newChar := s[i] + keyChar
|
||||
if newChar < 'a' {
|
||||
newChar = 'z' - ('a' - newChar) + 1
|
||||
}
|
||||
if newChar > 'z' {
|
||||
newChar = 'a' + (newChar - 'z') - 1
|
||||
}
|
||||
ret = ret + string(newChar)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// Decode decodes the given string
|
||||
func (c *VigenereCipher) Decode(s string) string {
|
||||
var ret string
|
||||
s = strings.ToLower(s)
|
||||
re := regexp.MustCompile("[^a-z]")
|
||||
s = re.ReplaceAllString(s, "")
|
||||
for i := range s {
|
||||
keyChar := c.key[i%len(c.key)] - 'a'
|
||||
newChar := s[i] - keyChar
|
||||
if newChar < 'a' {
|
||||
newChar = 'z' - ('a' - newChar) + 1
|
||||
}
|
||||
if newChar > 'z' {
|
||||
newChar = 'a' + (newChar - 'z') - 1
|
||||
}
|
||||
ret = ret + string(newChar)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// NewVigenere returns a new Vigenere Cipher
|
||||
func NewVigenere(key string) *VigenereCipher {
|
||||
re := regexp.MustCompile("[^a-z]")
|
||||
if len(key) < 3 || re.MatchString(key) {
|
||||
return nil
|
||||
}
|
||||
c := new(VigenereCipher)
|
||||
c.key = key
|
||||
return c
|
||||
}
|
270
go/simple-cipher/simple_cipher_test.go
Normal file
270
go/simple-cipher/simple_cipher_test.go
Normal file
@@ -0,0 +1,270 @@
|
||||
// For Step 1, implement the Caesar cipher, which seems clear enough except
|
||||
// maybe for figuring out whether to add or subtract.
|
||||
//
|
||||
// For Step 2, implement a shift cipher, like Caesar except the shift amount
|
||||
// is specified as an int. (Each letter of "ddddddddddddddddd", is 'd';
|
||||
// 'd'-'a' == 3, so the corresponding shift amount is 3.)
|
||||
//
|
||||
// Steps 2 and 3 seem to be describing the Vigenère cipher (Google it)
|
||||
// so let's do that too. The random thing, don't worry about. There is
|
||||
// no test for that.
|
||||
//
|
||||
// API:
|
||||
//
|
||||
// type Cipher interface {
|
||||
// Encode(string) string
|
||||
// Decode(string) string
|
||||
// }
|
||||
// NewCaesar() Cipher
|
||||
// NewShift(int) Cipher
|
||||
// NewVigenere(string) Cipher
|
||||
//
|
||||
// Interface Cipher is in file cipher.go.
|
||||
//
|
||||
// Argument for NewShift must be in the range 1 to 25 or -1 to -25.
|
||||
// Zero is disallowed. For invalid arguments NewShift returns nil.
|
||||
//
|
||||
// Argument for NewVigenere must consist of lower case letters a-z only.
|
||||
// Values consisting entirely of the letter 'a' are disallowed.
|
||||
// For invalid arguments NewVigenere returns nil.
|
||||
|
||||
package cipher
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// type for testing cipher encoding alone, without requiring any text prep.
|
||||
type prepped struct {
|
||||
pt string // prepped text == decoded plain text
|
||||
ct string // cipher text
|
||||
}
|
||||
|
||||
// +3? -3? Positions vary. Your code just has to pass the tests.
|
||||
var caesarPrepped = []prepped{
|
||||
{"iamapandabear", "ldpdsdqgdehdu"},
|
||||
{"programmingisawesome", "surjudpplqjlvdzhvrph"},
|
||||
{"todayisholiday", "wrgdblvkrolgdb"},
|
||||
{"venividivici", "yhqlylglylfl"},
|
||||
}
|
||||
|
||||
func TestCaesarPrepped(t *testing.T) {
|
||||
c := NewCaesar()
|
||||
for _, test := range caesarPrepped {
|
||||
if enc := c.Encode(test.pt); enc != test.ct {
|
||||
t.Fatalf("Caesar Encode(%q) = %q, want %q.", test.pt, enc, test.ct)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// type for testing implementations of the Cipher interface
|
||||
type cipherTest struct {
|
||||
source string // source text, any UTF-8
|
||||
cipher string // cipher text, result of Encode(st)
|
||||
plain string // decoded plain text, result of Decode(ct)
|
||||
}
|
||||
|
||||
var caesarTests = []cipherTest{
|
||||
{"Go, go, gophers", "jrjrjrskhuv", "gogogophers"},
|
||||
{"I am a panda bear.", "ldpdsdqgdehdu", "iamapandabear"},
|
||||
{"Programming is AWESOME!", "surjudpplqjlvdzhvrph", "programmingisawesome"},
|
||||
{"today is holiday", "wrgdblvkrolgdb", "todayisholiday"},
|
||||
{"Twas the night before Christmas",
|
||||
"wzdvwkhqljkwehiruhfkulvwpdv",
|
||||
"twasthenightbeforechristmas"},
|
||||
{"venividivici", "yhqlylglylfl", "venividivici"},
|
||||
{" -- @#!", "", ""},
|
||||
{"", "", ""},
|
||||
}
|
||||
|
||||
func TestCaesar(t *testing.T) {
|
||||
testCipher("Caesar", NewCaesar(), caesarTests, t)
|
||||
}
|
||||
|
||||
func testCipher(name string, c Cipher, tests []cipherTest, t *testing.T) {
|
||||
for _, test := range tests {
|
||||
if enc := c.Encode(test.source); enc != test.cipher {
|
||||
t.Fatalf("%s Encode(%q) = %q, want %q.",
|
||||
name, test.plain, enc, test.cipher)
|
||||
}
|
||||
if dec := c.Decode(test.cipher); dec != test.plain {
|
||||
t.Fatalf("%s Decode(%q) = %q, want %q.",
|
||||
name, test.cipher, dec, test.plain)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var NSATests = []cipherTest{
|
||||
{"THE ENEMY IS NEAR", "qebbkbjvfpkbxo", "theenemyisnear"},
|
||||
{"SPIES SEND SECRET MESSAGES",
|
||||
"pmfbppbkapbzobqjbppxdbp",
|
||||
"spiessendsecretmessages"},
|
||||
{"THOMAS JEFFERSON DESIGNED A SUBSTITUTION CIPHER",
|
||||
"qeljxpgbccboplkabpfdkbaxprypqfqrqflkzfmebo",
|
||||
"thomasjeffersondesignedasubstitutioncipher"},
|
||||
{"the quick brown fox jumps over the lazy dog",
|
||||
"qebnrfzhyoltkclugrjmplsboqebixwvald",
|
||||
"thequickbrownfoxjumpsoverthelazydog"},
|
||||
}
|
||||
|
||||
func TestShift(t *testing.T) {
|
||||
// test shift(3) against Caesar cases.
|
||||
c := NewShift(3)
|
||||
if c == nil {
|
||||
t.Fatal("NewShift(3) returned nil, want non-nil Cipher")
|
||||
}
|
||||
testCipher("Shift(3)", c, caesarTests, t)
|
||||
|
||||
// NSA and WP say Caesar uses shift of -3
|
||||
c = NewShift(-3)
|
||||
if c == nil {
|
||||
t.Fatal("NewShift(-3) returned nil, want non-nil Cipher")
|
||||
}
|
||||
testCipher("Shift(-3)", c, NSATests, t)
|
||||
|
||||
// invalid shifts
|
||||
for _, s := range []int{-27, -26, 0, 26, 27} {
|
||||
if NewShift(s) != nil {
|
||||
t.Fatal("NewShift(%d) returned non-nil, "+
|
||||
"Want nil return for invalid argument.", s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var vtests = []struct {
|
||||
key string
|
||||
tests []cipherTest
|
||||
}{
|
||||
{"lemon", []cipherTest{{"ATTACKATDAWN", "lxfopvefrnhr", "attackatdawn"}}},
|
||||
{"abcdefghij", []cipherTest{
|
||||
{"aaaaaaaaaa", "abcdefghij", "aaaaaaaaaa"},
|
||||
{"zzzzzzzzzz", "zabcdefghi", "zzzzzzzzzz"},
|
||||
}},
|
||||
{"iamapandabear", []cipherTest{
|
||||
{"I am a panda bear.", "qayaeaagaciai", "iamapandabear"},
|
||||
}},
|
||||
{"duxrceqyaimciuucnelkeoxjhdyduu", []cipherTest{
|
||||
{"Diffie Hellman", "gccwkixcltycv", "diffiehellman"},
|
||||
}},
|
||||
{"qgbvno", []cipherTest{
|
||||
{"cof-FEE, 123!", "sugars", "coffee"},
|
||||
}},
|
||||
}
|
||||
|
||||
func TestVigenere(t *testing.T) {
|
||||
for _, test := range vtests {
|
||||
v := NewVigenere(test.key)
|
||||
if v == nil {
|
||||
t.Fatalf("NewVigenere(%q) returned nil, want non-nil Cipher",
|
||||
test.key)
|
||||
}
|
||||
testCipher(fmt.Sprintf("Vigenere(%q)", test.key), v, test.tests, t)
|
||||
}
|
||||
|
||||
// invalid keys
|
||||
for _, k := range []string{"", "a", "aa", "no way", "CAT", "3", "and,"} {
|
||||
if NewVigenere(k) != nil {
|
||||
t.Fatalf("NewVigenere(%q) returned non-nil, "+
|
||||
"Want nil return for invalid argument.", k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Benchmark combined time to run all tests.
|
||||
// Note other ciphers test different data; times cannot be compared.
|
||||
func BenchmarkEncodeCaesar(b *testing.B) {
|
||||
c := NewCaesar()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
for _, test := range caesarTests {
|
||||
c.Encode(test.source)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDecodeCaesar(b *testing.B) {
|
||||
c := NewCaesar()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
for _, test := range caesarTests {
|
||||
c.Decode(test.cipher)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkNewShift(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
for s := -27; s <= 27; s++ {
|
||||
NewShift(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkEncodeShift(b *testing.B) {
|
||||
s := NewShift(5)
|
||||
all := append(caesarTests, NSATests...)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
for _, test := range all {
|
||||
s.Encode(test.source)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDecodeShift(b *testing.B) {
|
||||
s := NewShift(5)
|
||||
all := append(caesarTests, NSATests...)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
for _, test := range all {
|
||||
s.Decode(test.cipher)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkNewVigenere(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
for _, test := range vtests {
|
||||
NewVigenere(test.key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkEncVigenere(b *testing.B) {
|
||||
v := make([]Cipher, len(vtests))
|
||||
for i, test := range vtests {
|
||||
v[i] = NewVigenere(test.key)
|
||||
if v[i] == nil {
|
||||
b.Skip("Benchmark requires valid Vigenere test cases")
|
||||
}
|
||||
}
|
||||
b.ResetTimer()
|
||||
for j := 0; j < b.N; j++ {
|
||||
for i, test := range vtests {
|
||||
vi := v[i]
|
||||
for _, test := range test.tests {
|
||||
vi.Encode(test.source)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDecVigenere(b *testing.B) {
|
||||
v := make([]Cipher, len(vtests))
|
||||
for i, test := range vtests {
|
||||
v[i] = NewVigenere(test.key)
|
||||
if v[i] == nil {
|
||||
b.Skip("Benchmark requires valid Vigenere test cases")
|
||||
}
|
||||
}
|
||||
b.ResetTimer()
|
||||
for j := 0; j < b.N; j++ {
|
||||
for i, test := range vtests {
|
||||
vi := v[i]
|
||||
for _, test := range test.tests {
|
||||
vi.Decode(test.cipher)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user