|
1 |
| -package main |
| 1 | +// Package polybius is encrypting method with polybius square |
| 2 | +// ref: https://en.wikipedia.org/wiki/Polybius_square#Hybrid_Polybius_Playfair_Cipher |
| 3 | +package polybius |
2 | 4 |
|
3 | 5 | import (
|
4 |
| - "errors" |
| 6 | + "fmt" |
| 7 | + "math" |
5 | 8 | "strings"
|
6 | 9 | )
|
7 | 10 |
|
8 |
| -type polybius struct { |
| 11 | +// Polybius is struct having size, characters, and key |
| 12 | +type Polybius struct { |
9 | 13 | size int
|
10 | 14 | characters string
|
11 | 15 | key string
|
12 | 16 | }
|
13 | 17 |
|
14 |
| -func newPolybius(key string, size int, chars string) (*polybius, error) { |
| 18 | +// NewPolybius returns a pointer to object of Polybius. |
| 19 | +// If the size of "chars" is longer than "size", |
| 20 | +// "chars" are truncated to "size". |
| 21 | +func NewPolybius(key string, size int, chars string) (*Polybius, error) { |
15 | 22 | key = strings.ToUpper(key)
|
16 | 23 | chars = strings.ToUpper(chars)[:size]
|
17 |
| - if len(chars) != size { |
18 |
| - return nil, errors.New("chars must be as long as size") |
| 24 | + for idx, ch := range chars { |
| 25 | + if strings.Contains(chars[idx+1:], string(ch)) { |
| 26 | + return nil, fmt.Errorf("\"chars\" contains same character: %c", ch) |
| 27 | + } |
19 | 28 | }
|
| 29 | + |
20 | 30 | if len(key) != size*size {
|
21 |
| - return nil, errors.New("key must be as long as the size squared") |
| 31 | + return nil, fmt.Errorf("len(key): %d must be as long as size squared: %d", len(key), size*size) |
22 | 32 | }
|
23 |
| - return &polybius{size, chars, key}, nil |
| 33 | + return &Polybius{size, chars, key}, nil |
24 | 34 | }
|
25 | 35 |
|
26 |
| -func (p *polybius) encrypt(text string) string { |
| 36 | +// Encrypt encrypts with polybius encryption |
| 37 | +func (p *Polybius) Encrypt(text string) (string, error) { |
27 | 38 | chars := []rune(strings.ToUpper(text))
|
28 | 39 | encryptedText := ""
|
29 | 40 | for _, char := range chars {
|
30 |
| - encryptedText += p.encipher(char) |
| 41 | + encryptedChar, err := p.encipher(char) |
| 42 | + if err != nil { |
| 43 | + return "", fmt.Errorf("failed encipher: %w", err) |
| 44 | + } |
| 45 | + encryptedText += encryptedChar |
31 | 46 | }
|
32 |
| - return encryptedText |
| 47 | + return encryptedText, nil |
33 | 48 | }
|
34 | 49 |
|
35 |
| -func (p *polybius) decrypt(text string) string { |
| 50 | +// Decrypt decrypts with polybius encryption |
| 51 | +func (p *Polybius) Decrypt(text string) (string, error) { |
36 | 52 | chars := []rune(strings.ToUpper(text))
|
37 | 53 | decryptedText := ""
|
38 | 54 | for i := 0; i < len(chars); i += 2 {
|
39 |
| - decryptedText += p.decipher(chars[i : i+2]) |
| 55 | + decryptedChar, err := p.decipher(chars[i:int(math.Min(float64(i+2), float64(len(chars))))]) |
| 56 | + if err != nil { |
| 57 | + return "", fmt.Errorf("failed decipher: %w", err) |
| 58 | + } |
| 59 | + decryptedText += decryptedChar |
40 | 60 | }
|
41 |
| - return decryptedText |
| 61 | + return decryptedText, nil |
42 | 62 | }
|
43 | 63 |
|
44 |
| -func (p *polybius) encipher(char rune) string { |
| 64 | +func (p *Polybius) encipher(char rune) (string, error) { |
45 | 65 | index := strings.IndexRune(p.key, char)
|
| 66 | + if index < 0 { |
| 67 | + return "", fmt.Errorf("%c does not exist in keys", char) |
| 68 | + } |
46 | 69 | row := index / p.size
|
47 | 70 | col := index % p.size
|
48 | 71 | chars := []rune(p.characters)
|
49 |
| - return string([]rune{chars[row], chars[col]}) |
| 72 | + return string([]rune{chars[row], chars[col]}), nil |
50 | 73 | }
|
51 | 74 |
|
52 |
| -func (p *polybius) decipher(chars []rune) string { |
| 75 | +func (p *Polybius) decipher(chars []rune) (string, error) { |
53 | 76 | if len(chars) != 2 {
|
54 |
| - panic("decipher takes two chars") |
| 77 | + return "", fmt.Errorf("the size of \"chars\" must be even") |
55 | 78 | }
|
56 | 79 | row := strings.IndexRune(p.characters, chars[0])
|
| 80 | + if row < 0 { |
| 81 | + return "", fmt.Errorf("%c does not exist in characters", chars[0]) |
| 82 | + } |
57 | 83 | col := strings.IndexRune(p.characters, chars[1])
|
58 |
| - return string([]rune(p.key)[row*p.size+col]) |
| 84 | + if col < 0 { |
| 85 | + return "", fmt.Errorf("%c does not exist in characters", chars[1]) |
| 86 | + } |
| 87 | + return string([]rune(p.key)[row*p.size+col]), nil |
59 | 88 | }
|
0 commit comments