1
0
mirror of https://github.com/golang/go synced 2024-09-25 05:10:12 -06:00

crypto/openpgp: add s2k

s2k implements the string-to-key functions for OpenPGP

R=rsc
CC=golang-dev
https://golang.org/cl/3937043
This commit is contained in:
Adam Langley 2011-01-20 07:38:34 -05:00
parent 6d94b14e19
commit 0bec484e6f
3 changed files with 251 additions and 0 deletions

View File

@ -0,0 +1,11 @@
# Copyright 2011 The Go Authors. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
include ../../../../Make.inc
TARG=crypto/openpgp/s2k
GOFILES=\
s2k.go\
include ../../../../Make.pkg

View File

@ -0,0 +1,146 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This package implements the various OpenPGP string-to-key transforms as
// specified in RFC 4800 section 3.7.1.
package s2k
import (
"crypto/md5"
"crypto/openpgp/error"
"crypto/ripemd160"
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"hash"
"io"
"os"
)
// Simple writes to out the result of computing the Simple S2K function (RFC
// 4880, section 3.7.1.1) using the given hash and input passphrase.
func Simple(out []byte, h hash.Hash, in []byte) {
Salted(out, h, in, nil)
}
var zero [1]byte
// Salted writes to out the result of computing the Salted S2K function (RFC
// 4880, section 3.7.1.2) using the given hash, input passphrase and salt.
func Salted(out []byte, h hash.Hash, in []byte, salt []byte) {
done := 0
for i := 0; done < len(out); i++ {
h.Reset()
for j := 0; j < i; j++ {
h.Write(zero[:])
}
h.Write(salt)
h.Write(in)
n := copy(out[done:], h.Sum())
done += n
}
}
// Iterated writes to out the result of computing the Iterated and Salted S2K
// function (RFC 4880, section 3.7.1.3) using the given hash, input passphrase,
// salt and iteration count.
func Iterated(out []byte, h hash.Hash, in []byte, salt []byte, count int) {
combined := make([]byte, len(in)+len(salt))
copy(combined, salt)
copy(combined[len(salt):], in)
if count < len(combined) {
count = len(combined)
}
done := 0
for i := 0; done < len(out); i++ {
h.Reset()
for j := 0; j < i; j++ {
h.Write(zero[:])
}
written := 0
for written < count {
if written+len(combined) > count {
todo := count - written
h.Write(combined[:todo])
written = count
} else {
h.Write(combined)
written += len(combined)
}
}
n := copy(out[done:], h.Sum())
done += n
}
}
// Parse reads a binary specification for a string-to-key transformation from r
// and returns a function which performs that transform.
func Parse(r io.Reader) (f func(out, in []byte), err os.Error) {
var buf [9]byte
_, err = io.ReadFull(r, buf[:2])
if err != nil {
return
}
h := hashFuncFromType(buf[1])
if h == nil {
return nil, error.UnsupportedError("hash for S2K function")
}
switch buf[0] {
case 1:
f := func(out, in []byte) {
Simple(out, h, in)
}
return f, nil
case 2:
_, err := io.ReadFull(r, buf[:8])
if err != nil {
return
}
f := func(out, in []byte) {
Salted(out, h, in, buf[:8])
}
return f, nil
case 3:
_, err := io.ReadFull(r, buf[:9])
if err != nil {
return
}
count := (16 + int(buf[8]&15)) << (uint32(buf[8]>>4) + 6)
f := func(out, in []byte) {
Iterated(out, h, in, buf[:8], count)
}
return f, nil
}
return nil, error.UnsupportedError("S2K function")
}
// hashFuncFromType returns a hash.Hash which corresponds to the given hash
// type byte. See RFC 4880, section 9.4.
func hashFuncFromType(hashType byte) hash.Hash {
switch hashType {
case 1:
return md5.New()
case 2:
return sha1.New()
case 3:
return ripemd160.New()
case 8:
return sha256.New()
case 9:
return sha512.New384()
case 10:
return sha512.New()
case 11:
return sha256.New224()
}
return nil
}

View File

@ -0,0 +1,94 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package s2k
import (
"bytes"
"crypto/sha1"
"encoding/hex"
"testing"
)
var saltedTests = []struct {
in, out string
}{
{"hello", "10295ac1"},
{"world", "ac587a5e"},
{"foo", "4dda8077"},
{"bar", "bd8aac6b9ea9cae04eae6a91c6133b58b5d9a61c14f355516ed9370456"},
{"x", "f1d3f289"},
{"xxxxxxxxxxxxxxxxxxxxxxx", "e00d7b45"},
}
func TestSalted(t *testing.T) {
h := sha1.New()
salt := [4]byte{1, 2, 3, 4}
for i, test := range saltedTests {
expected, _ := hex.DecodeString(test.out)
out := make([]byte, len(expected))
Salted(out, h, []byte(test.in), salt[:])
if !bytes.Equal(expected, out) {
t.Errorf("#%d, got: %x want: %x", i, out, expected)
}
}
}
var iteratedTests = []struct {
in, out string
}{
{"hello", "83126105"},
{"world", "6fa317f9"},
{"foo", "8fbc35b9"},
{"bar", "2af5a99b54f093789fd657f19bd245af7604d0f6ae06f66602a46a08ae"},
{"x", "5a684dfe"},
{"xxxxxxxxxxxxxxxxxxxxxxx", "18955174"},
}
func TestIterated(t *testing.T) {
h := sha1.New()
salt := [4]byte{4, 3, 2, 1}
for i, test := range iteratedTests {
expected, _ := hex.DecodeString(test.out)
out := make([]byte, len(expected))
Iterated(out, h, []byte(test.in), salt[:], 31)
if !bytes.Equal(expected, out) {
t.Errorf("#%d, got: %x want: %x", i, out, expected)
}
}
}
var parseTests = []struct {
spec, in, out string
}{
/* Simple with SHA1 */
{"0102", "hello", "aaf4c61d"},
/* Salted with SHA1 */
{"02020102030405060708", "hello", "f4f7d67e"},
/* Iterated with SHA1 */
{"03020102030405060708f1", "hello", "f2a57b7c"},
}
func TestParse(t *testing.T) {
for i, test := range parseTests {
spec, _ := hex.DecodeString(test.spec)
buf := bytes.NewBuffer(spec)
f, err := Parse(buf)
if err != nil {
t.Errorf("%d: Parse returned error: %s", i, err)
continue
}
expected, _ := hex.DecodeString(test.out)
out := make([]byte, len(expected))
f(out, []byte(test.in))
if !bytes.Equal(out, expected) {
t.Errorf("%d: output got: %x want: %x", i, out, expected)
}
}
}