-
Notifications
You must be signed in to change notification settings - Fork 26
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
331 additions
and
3 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
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,59 @@ | ||
package pgp_s2k | ||
|
||
var s2kIters = [256]uint32{ | ||
1024, 1088, 1152, 1216, 1280, 1344, 1408, 1472, 1536, 1600, 1664, | ||
1728, 1792, 1856, 1920, 1984, 2048, 2176, 2304, 2432, 2560, 2688, | ||
2816, 2944, 3072, 3200, 3328, 3456, 3584, 3712, 3840, 3968, 4096, | ||
4352, 4608, 4864, 5120, 5376, 5632, 5888, 6144, 6400, 6656, 6912, | ||
7168, 7424, 7680, 7936, 8192, 8704, 9216, 9728, 10240, 10752, 11264, | ||
11776, 12288, 12800, 13312, 13824, 14336, 14848, 15360, 15872, 16384, 17408, | ||
18432, 19456, 20480, 21504, 22528, 23552, 24576, 25600, 26624, 27648, 28672, | ||
29696, 30720, 31744, 32768, 34816, 36864, 38912, 40960, 43008, 45056, 47104, | ||
49152, 51200, 53248, 55296, 57344, 59392, 61440, 63488, 65536, 69632, 73728, | ||
77824, 81920, 86016, 90112, 94208, 98304, 102400, 106496, 110592, 114688, 118784, | ||
122880, 126976, 131072, 139264, 147456, 155648, 163840, 172032, 180224, 188416, 196608, | ||
204800, 212992, 221184, 229376, 237568, 245760, 253952, 262144, 278528, 294912, 311296, | ||
327680, 344064, 360448, 376832, 393216, 409600, 425984, 442368, 458752, 475136, 491520, | ||
507904, 524288, 557056, 589824, 622592, 655360, 688128, 720896, 753664, 786432, 819200, | ||
851968, 884736, 917504, 950272, 983040, 1015808, 1048576, 1114112, 1179648, 1245184, 1310720, | ||
1376256, 1441792, 1507328, 1572864, 1638400, 1703936, 1769472, 1835008, 1900544, 1966080, 2031616, | ||
2097152, 2228224, 2359296, 2490368, 2621440, 2752512, 2883584, 3014656, 3145728, 3276800, 3407872, | ||
3538944, 3670016, 3801088, 3932160, 4063232, 4194304, 4456448, 4718592, 4980736, 5242880, 5505024, | ||
5767168, 6029312, 6291456, 6553600, 6815744, 7077888, 7340032, 7602176, 7864320, 8126464, 8388608, | ||
8912896, 9437184, 9961472, 10485760, 11010048, 11534336, 12058624, 12582912, 13107200, 13631488, 14155776, | ||
14680064, 15204352, 15728640, 16252928, 16777216, 17825792, 18874368, 19922944, 20971520, 22020096, 23068672, | ||
24117248, 25165824, 26214400, 27262976, 28311552, 29360128, 30408704, 31457280, 32505856, 33554432, 35651584, | ||
37748736, 39845888, 41943040, 44040192, 46137344, 48234496, 50331648, 52428800, 54525952, 56623104, 58720256, | ||
60817408, 62914560, 65011712}; | ||
|
||
func encodeCount(desiredIterations uint32) uint8 { | ||
if desiredIterations <= s2kIters[0] { | ||
return 0 | ||
} | ||
|
||
if desiredIterations >= s2kIters[255] { | ||
return 255 | ||
} | ||
|
||
i := lowerBound(s2kIters[:], desiredIterations) | ||
|
||
return uint8(i) | ||
} | ||
|
||
func decodeCount(iter uint8) uint32 { | ||
return s2kIters[iter] | ||
} | ||
|
||
func roundIterations(iterations uint32) uint32 { | ||
return decodeCount(encodeCount(iterations)) | ||
} | ||
|
||
func lowerBound(s2kIters []uint32, iter uint32) int { | ||
for k, v := range s2kIters { | ||
if v >= iter { | ||
return k | ||
} | ||
} | ||
|
||
return len(s2kIters) | ||
} |
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,92 @@ | ||
package pgp_s2k | ||
|
||
import ( | ||
"time" | ||
"hash" | ||
"bytes" | ||
) | ||
|
||
/** | ||
* OpenPGP's S2K | ||
* | ||
* See RFC 4880 sections 3.7.1.1, 3.7.1.2, and 3.7.1.3 | ||
* If the salt is empty and iterations == 1, "simple" S2K is used | ||
* If the salt is non-empty and iterations == 1, "salted" S2K is used | ||
* If the salt is non-empty and iterations > 1, "iterated" S2K is used | ||
* | ||
* Due to complexities of the PGP S2K algorithm, time-based derivation | ||
* is not supported. So if iterations == 0 and msec.count() > 0, an | ||
* exception is thrown. In the future this may be supported, in which | ||
* case "iterated" S2K will be used and the number of iterations | ||
* performed is returned. | ||
*/ | ||
func Key(h func() hash.Hash, password, salt []byte, iterations, keylen int) []byte { | ||
if (iterations > 1 && len(salt) == 0) { | ||
panic("go-cryptobin/pgp_s2k: OpenPGP S2K requires a salt in iterated mode") | ||
} | ||
|
||
newHash := h() | ||
|
||
inputBuf := make([]byte, len(salt) + len(password)) | ||
copy(inputBuf[:], salt) | ||
copy(inputBuf[len(salt):], password) | ||
|
||
var hashBuf []byte | ||
var outputBuf []byte | ||
|
||
var pass int = 0 | ||
var generated int = 0 | ||
|
||
for generated < keylen { | ||
output_this_pass := mathMin(newHash.Size(), keylen - generated) | ||
|
||
newHash.Reset() | ||
|
||
// Preload some number of zero bytes (empty first iteration) | ||
zero_padding := bytes.Repeat([]byte{0}, pass) | ||
newHash.Write(zero_padding) | ||
|
||
// The input is always fully processed even if iterations is very small | ||
if len(inputBuf) > 0 { | ||
left := mathMax(iterations, len(inputBuf)) | ||
for left > 0 { | ||
input_to_take := mathMin(left, len(inputBuf)) | ||
newHash.Write(inputBuf[:input_to_take]) | ||
left -= input_to_take | ||
} | ||
} | ||
|
||
hashBuf = newHash.Sum(nil) | ||
|
||
outputBuf = append(outputBuf, hashBuf[:output_this_pass]...) | ||
|
||
generated += output_this_pass | ||
pass++ | ||
} | ||
|
||
return outputBuf | ||
} | ||
|
||
func KeyWithTune(h func() hash.Hash, password, salt []byte, iterations, keylen int, msec time.Duration) []byte { | ||
if iterations == 0 { | ||
iterations = tune(h(), keylen, msec, 0, 10 * time.Millisecond) | ||
} | ||
|
||
return Key(h, password, salt, iterations, keylen) | ||
} | ||
|
||
func mathMin(a, b int) int { | ||
if a < b { | ||
return a | ||
} | ||
|
||
return b | ||
} | ||
|
||
func mathMax(a, b int) int { | ||
if a > b { | ||
return a | ||
} | ||
|
||
return b | ||
} |
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,124 @@ | ||
package pgp_s2k | ||
|
||
import ( | ||
"hash" | ||
"bytes" | ||
"testing" | ||
"crypto/sha1" | ||
"crypto/sha512" | ||
"encoding/hex" | ||
) | ||
|
||
func fromHex(s string) []byte { | ||
h, _ := hex.DecodeString(s) | ||
return h | ||
} | ||
|
||
type testVector struct { | ||
password string | ||
salt []byte | ||
iter int | ||
output []byte | ||
} | ||
|
||
var sha1TestVectors = []testVector{ | ||
{ | ||
"", | ||
fromHex(""), | ||
1, | ||
fromHex("DA39A3EE5E6B"), | ||
}, | ||
{ | ||
"hello", | ||
fromHex(""), | ||
1, | ||
fromHex("AAF4C61D"), | ||
}, | ||
{ | ||
"hello", | ||
fromHex("01020304"), | ||
1, | ||
fromHex("10295AC1"), | ||
}, | ||
{ | ||
"bar", | ||
fromHex("01020304"), | ||
1, | ||
fromHex("BD8AAC6B9EA9CAE04EAE6A91C6133B58B5D9A61C14F355516ED9370456"), | ||
}, | ||
{ | ||
"bar", | ||
fromHex("04030201"), | ||
31, | ||
fromHex("2AF5A99B54F093789FD657F19BD245AF7604D0F6AE06F66602A46A08AE"), | ||
}, | ||
{ | ||
"ilikepie", | ||
fromHex("2AE6E5831A717917"), | ||
65536, | ||
fromHex("A32F874A4CF95DFAD8359302B395455C"), | ||
}, | ||
{ | ||
"passphrase", | ||
fromHex("0102030405060708"), | ||
1, | ||
fromHex("eec8929a31187dd3a9a7ce5a97d96a67706382bbef70fe3b2a3aeeaf176bce252c117970a51fd8b770a69f8ecb199505395bd7b0c0760d6a38ac82900b23fe3b"), | ||
}, | ||
{ | ||
"passphrase", | ||
fromHex("0102030405060708"), | ||
65536, | ||
fromHex("24ce08a4a31de2208acdc15347def7a63492d38a0c08f80533a746279d91cb25e1b6b740b09e20a4884ca1944d506eb200753761066e8d4b24957c2593388457"), | ||
}, | ||
{ | ||
"passphrase", | ||
fromHex("0102030405060708"), | ||
10000000, | ||
fromHex("09efbd3599e2453c6cf1749a7ed169514a12d1a721549468c6d0ef6737fa3e27ab6d100f9839694fc70c484a42b00ef87463d07e2aafb92033843a4bd5f37971"), | ||
}, | ||
} | ||
|
||
var sha384TestVectors = []testVector{ | ||
{ | ||
"passphrase", | ||
fromHex("0102030405060708"), | ||
1, | ||
fromHex("ea024c2de8af9a3edfbac9422f7b17e17c3165147b43f4edd58b55af9a412d07e0631f431a7e0028fbb145d9d5e059a888f1a7526cd338b1b6082a8681b446fa"), | ||
}, | ||
{ | ||
"passphrase", | ||
fromHex("0102030405060708"), | ||
18, | ||
fromHex("ea024c2de8af9a3edfbac9422f7b17e17c3165147b43f4edd58b55af9a412d07e0631f431a7e0028fbb145d9d5e059a888f1a7526cd338b1b6082a8681b446fa"), | ||
}, | ||
{ | ||
"passphrase", | ||
fromHex("0102030405060708"), | ||
19, | ||
fromHex("491aa377a1a9526d53b118587a03f85f5dc64568f3aabaad66aafc923397fb74d7017a6a4812bff2d9beddcbc6a0dbd3b96a3f9a69b637d68670acd48d4dfa4e"), | ||
}, | ||
{ | ||
"passphrase", | ||
fromHex("0102030405060708"), | ||
1000000, | ||
fromHex("af4986488f4e53ac4f7991a0bb8de15441ba1070481fe63b126ff3de2e1072f568dd3d6c5887d008925d27649494ae6f4860e141d5eeabe4f93745ca9cb8e08c"), | ||
}, | ||
|
||
} | ||
|
||
func testHash(t *testing.T, h func() hash.Hash, hashName string, vectors []testVector) { | ||
for i, v := range vectors { | ||
o := Key(h, []byte(v.password), v.salt, v.iter, len(v.output)) | ||
if !bytes.Equal(o, v.output) { | ||
t.Errorf("%s %d: want %x, got %x", hashName, i, v.output, o) | ||
} | ||
} | ||
} | ||
|
||
func TestWithSHA1(t *testing.T) { | ||
testHash(t, sha1.New, "SHA1", sha1TestVectors) | ||
} | ||
|
||
func TestWithSHA384(t *testing.T) { | ||
testHash(t, sha512.New384, "SHA384", sha384TestVectors) | ||
} |
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,53 @@ | ||
package pgp_s2k | ||
|
||
import ( | ||
"time" | ||
"hash" | ||
) | ||
|
||
func tune(h hash.Hash, keylen int, msec time.Duration, _ int, tune_time time.Duration) int { | ||
var buf_size int = 1024 | ||
var buffer = make([]byte, buf_size) | ||
var time_used uint64 = 0 | ||
var event_count uint64 = 0 | ||
|
||
td := time.Duration(buf_size) | ||
|
||
timer := time.NewTimer(td) | ||
for { | ||
select { | ||
case <-timer.C: | ||
event_count++ | ||
time_used = time_used + uint64(td) | ||
|
||
h.Write(buffer) | ||
|
||
if time.Duration(time_used) < tune_time { | ||
timer.Reset(td) | ||
} | ||
} | ||
} | ||
|
||
var hash_bytes_per_second uint64 | ||
if td.Seconds() > 0 { | ||
hash_bytes_per_second = (uint64(buf_size) * event_count) / uint64(td.Seconds()) | ||
} else { | ||
hash_bytes_per_second = 0 | ||
} | ||
|
||
desired_nsec := uint64(msec.Nanoseconds()) | ||
|
||
hash_size := h.Size() | ||
|
||
var blocks_required int | ||
if keylen <= hash_size { | ||
blocks_required = 1 | ||
} else { | ||
blocks_required = (keylen + hash_size - 1) / hash_size | ||
} | ||
|
||
bytes_to_be_hashed := (hash_bytes_per_second * (desired_nsec / 1000000000)) / uint64(blocks_required) | ||
iterations := roundIterations(uint32(bytes_to_be_hashed)) | ||
|
||
return int(iterations) | ||
} |