pkcs8: support legacy PBES1 algorithms (parse only)

This commit is contained in:
Sun Yimin 2024-07-05 16:07:38 +08:00 committed by GitHub
parent ff1269c9dc
commit 41f4ec0e83
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 782 additions and 16 deletions

158
pkcs/internal/md2/md2.go Normal file
View File

@ -0,0 +1,158 @@
// Package md2 implements the MD2 hash algorithm as defined in RFC 1319.
//
// MD2 is cryptographically broken and should not be used for secure
// applications.
package md2
import "hash"
// Size the size of a MD2 checksum in bytes.
const Size int = 16
// SizeBitSize the bit size of Size.
const SizeBitSize = 4
// BlockSize the blocksize of MD2 in bytes.
const BlockSize int = 16
var piSubst = [256]byte{
0x29, 0x2E, 0x43, 0xC9, 0xA2, 0xD8, 0x7C, 0x01, 0x3D, 0x36, 0x54, 0xA1, 0xEC, 0xF0, 0x06, 0x13,
0x62, 0xA7, 0x05, 0xF3, 0xC0, 0xC7, 0x73, 0x8C, 0x98, 0x93, 0x2B, 0xD9, 0xBC, 0x4C, 0x82, 0xCA,
0x1E, 0x9B, 0x57, 0x3C, 0xFD, 0xD4, 0xE0, 0x16, 0x67, 0x42, 0x6F, 0x18, 0x8A, 0x17, 0xE5, 0x12,
0xBE, 0x4E, 0xC4, 0xD6, 0xDA, 0x9E, 0xDE, 0x49, 0xA0, 0xFB, 0xF5, 0x8E, 0xBB, 0x2F, 0xEE, 0x7A,
0xA9, 0x68, 0x79, 0x91, 0x15, 0xB2, 0x07, 0x3F, 0x94, 0xC2, 0x10, 0x89, 0x0B, 0x22, 0x5F, 0x21,
0x80, 0x7F, 0x5D, 0x9A, 0x5A, 0x90, 0x32, 0x27, 0x35, 0x3E, 0xCC, 0xE7, 0xBF, 0xF7, 0x97, 0x03,
0xFF, 0x19, 0x30, 0xB3, 0x48, 0xA5, 0xB5, 0xD1, 0xD7, 0x5E, 0x92, 0x2A, 0xAC, 0x56, 0xAA, 0xC6,
0x4F, 0xB8, 0x38, 0xD2, 0x96, 0xA4, 0x7D, 0xB6, 0x76, 0xFC, 0x6B, 0xE2, 0x9C, 0x74, 0x04, 0xF1,
0x45, 0x9D, 0x70, 0x59, 0x64, 0x71, 0x87, 0x20, 0x86, 0x5B, 0xCF, 0x65, 0xE6, 0x2D, 0xA8, 0x02,
0x1B, 0x60, 0x25, 0xAD, 0xAE, 0xB0, 0xB9, 0xF6, 0x1C, 0x46, 0x61, 0x69, 0x34, 0x40, 0x7E, 0x0F,
0x55, 0x47, 0xA3, 0x23, 0xDD, 0x51, 0xAF, 0x3A, 0xC3, 0x5C, 0xF9, 0xCE, 0xBA, 0xC5, 0xEA, 0x26,
0x2C, 0x53, 0x0D, 0x6E, 0x85, 0x28, 0x84, 0x09, 0xD3, 0xDF, 0xCD, 0xF4, 0x41, 0x81, 0x4D, 0x52,
0x6A, 0xDC, 0x37, 0xC8, 0x6C, 0xC1, 0xAB, 0xFA, 0x24, 0xE1, 0x7B, 0x08, 0x0C, 0xBD, 0xB1, 0x4A,
0x78, 0x88, 0x95, 0x8B, 0xE3, 0x63, 0xE8, 0x6D, 0xE9, 0xCB, 0xD5, 0xFE, 0x3B, 0x00, 0x1D, 0x39,
0xF2, 0xEF, 0xB7, 0x0E, 0x66, 0x58, 0xD0, 0xE4, 0xA6, 0x77, 0x72, 0xF8, 0xEB, 0x75, 0x4B, 0x0A,
0x31, 0x44, 0x50, 0xB4, 0x8F, 0xED, 0x1F, 0x1A, 0xDB, 0x99, 0x8D, 0x33, 0x9F, 0x11, 0x83, 0x14,
}
// digest represents the partial evaluation of a checksum.
type digest struct {
s [Size]byte // state
c [BlockSize]byte // checksum
x [BlockSize]byte // buffer
nx int
len uint64
}
func (d *digest) Reset() {
for i := range d.s {
d.s[i] = 0
}
for i := range d.c {
d.c[i] = 0
}
d.nx = 0
d.len = 0
}
// New returns a new hash.Hash computing the MD2 checksum.
func New() hash.Hash {
d := new(digest)
d.Reset()
return d
}
func (d *digest) Size() int { return Size }
func (d *digest) BlockSize() int { return BlockSize }
func (d *digest) Write(p []byte) (nn int, err error) {
nn = len(p)
d.len += uint64(nn)
if d.nx > 0 {
n := copy(d.x[d.nx:], p)
d.nx += n
if d.nx == BlockSize {
block(d, d.x[:])
d.nx = 0
}
p = p[n:]
}
if len(p) >= BlockSize {
n := len(p) &^ (BlockSize - 1)
block(d, p[:n])
p = p[n:]
}
if len(p) > 0 {
d.nx = copy(d.x[:], p)
}
return
}
func (d *digest) Sum(in []byte) []byte {
// Make a copy of d so that caller can keep writing and summing.
d0 := *d
hash := d0.checkSum()
return append(in, hash[:]...)
}
func (d *digest) checkSum() [Size]byte {
// Pad out to multiple of BlockSize bytes.
var tmp [BlockSize]byte
padLen := BlockSize - d.nx
for i := 0; i < padLen; i++ {
tmp[i] = byte(padLen)
}
d.Write(tmp[:padLen])
// The previous write ensures that a whole number of
// blocks (i.e. a multiple of 16 bytes) have been hashed.
if d.nx != 0 {
panic("d.nx != 0")
}
// Append the checksum
d.Write(d.c[:])
var digest [Size]byte
copy(digest[:], d.s[:])
return digest
}
func block(dig *digest, p []byte) {
var X [48]byte
for i := 0; i <= len(p)-BlockSize; i += BlockSize {
// Form encryption block from state, block, and state ^ block.
copy(X[:16], dig.s[:])
copy(X[16:32], p[i:i+BlockSize])
for j := 0; j < BlockSize; j++ {
X[32+j] = X[16+j] ^ X[j]
}
// Encrypt block (18 rounds)
t := byte(0)
for j := 0; j < 18; j++ {
for k := 0; k < 48; k++ {
X[k] ^= piSubst[t]
t = X[k]
}
t = t + byte(j)
}
// Save state
copy(dig.s[:], X[:16])
// Update checksum
t = dig.c[15]
for j := 0; j < 16; j++ {
dig.c[j] ^= piSubst[p[i+j]^t]
t = dig.c[j]
}
}
}
// Sum returns the MD2 checksum of the data.
func Sum(data []byte) [Size]byte {
var d digest
d.Reset()
d.Write(data)
return d.checkSum()
}

View File

@ -0,0 +1,32 @@
package md2
import (
"fmt"
"testing"
)
type md2Test struct {
out string
in string
}
var golden = []md2Test{
{"8350e5a3e24c153df2275c9f80692773", ""},
{"32ec01ec4a6dac72c0ab96fb34c0b5d1", "a"},
{"da853b0d3f88d99b30283a69e6ded6bb", "abc"},
{"ab4f496bfb2a530b219ff33031fe06b0", "message digest"},
{"4e8ddff3650292ab5a4108c3aa47940b", "abcdefghijklmnopqrstuvwxyz"},
{"da33def2a42df13975352846c30338cd", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"},
{"d5976f79d83d3a0dc9806c3c66f3efd8", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"},
}
func TestGolden(t *testing.T) {
for i := 0; i < len(golden); i++ {
g := golden[i]
h := Sum([]byte(g.in))
s := fmt.Sprintf("%x", h)
if s != g.out {
t.Fatalf("MD2 function: md2(%s) = %s want %s", g.in, s, g.out)
}
}
}

308
pkcs/internal/rc2/rc2.go Normal file
View File

@ -0,0 +1,308 @@
// Copyright 2015 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 rc2 implements the RC2 cipher
/*
https://www.ietf.org/rfc/rfc2268.txt
http://people.csail.mit.edu/rivest/pubs/KRRR98.pdf
This code is licensed under the MIT license.
*/
package rc2
import (
"crypto/cipher"
"encoding/binary"
"fmt"
"github.com/emmansun/gmsm/internal/alias"
)
// The rc2 block size in bytes
const BlockSize = 8
type rc2Cipher struct {
k [64]uint16
}
// NewCipherWithEffectiveKeyBits returns a new rc2 cipher with the given key and effective key length in bits t1
func NewCipherWithEffectiveKeyBits(key []byte, t1 int) (cipher.Block, error) {
kLen := len(key)
if kLen < 1 || kLen > 128 {
return nil, fmt.Errorf("rc2: invalid key size %d", kLen)
}
if t1 < 1 || t1 > 1024 {
return nil, fmt.Errorf("rc2: invalid effective key length %d", t1)
}
return &rc2Cipher{
k: expandKey(key, t1),
}, nil
}
// NewCipher returns a new rc2 cipher with the given key
func NewCipher(key []byte) (cipher.Block, error) {
return NewCipherWithEffectiveKeyBits(key, len(key)*8)
}
func (*rc2Cipher) BlockSize() int { return BlockSize }
var piTable = [256]byte{
0xd9, 0x78, 0xf9, 0xc4, 0x19, 0xdd, 0xb5, 0xed, 0x28, 0xe9, 0xfd, 0x79, 0x4a, 0xa0, 0xd8, 0x9d,
0xc6, 0x7e, 0x37, 0x83, 0x2b, 0x76, 0x53, 0x8e, 0x62, 0x4c, 0x64, 0x88, 0x44, 0x8b, 0xfb, 0xa2,
0x17, 0x9a, 0x59, 0xf5, 0x87, 0xb3, 0x4f, 0x13, 0x61, 0x45, 0x6d, 0x8d, 0x09, 0x81, 0x7d, 0x32,
0xbd, 0x8f, 0x40, 0xeb, 0x86, 0xb7, 0x7b, 0x0b, 0xf0, 0x95, 0x21, 0x22, 0x5c, 0x6b, 0x4e, 0x82,
0x54, 0xd6, 0x65, 0x93, 0xce, 0x60, 0xb2, 0x1c, 0x73, 0x56, 0xc0, 0x14, 0xa7, 0x8c, 0xf1, 0xdc,
0x12, 0x75, 0xca, 0x1f, 0x3b, 0xbe, 0xe4, 0xd1, 0x42, 0x3d, 0xd4, 0x30, 0xa3, 0x3c, 0xb6, 0x26,
0x6f, 0xbf, 0x0e, 0xda, 0x46, 0x69, 0x07, 0x57, 0x27, 0xf2, 0x1d, 0x9b, 0xbc, 0x94, 0x43, 0x03,
0xf8, 0x11, 0xc7, 0xf6, 0x90, 0xef, 0x3e, 0xe7, 0x06, 0xc3, 0xd5, 0x2f, 0xc8, 0x66, 0x1e, 0xd7,
0x08, 0xe8, 0xea, 0xde, 0x80, 0x52, 0xee, 0xf7, 0x84, 0xaa, 0x72, 0xac, 0x35, 0x4d, 0x6a, 0x2a,
0x96, 0x1a, 0xd2, 0x71, 0x5a, 0x15, 0x49, 0x74, 0x4b, 0x9f, 0xd0, 0x5e, 0x04, 0x18, 0xa4, 0xec,
0xc2, 0xe0, 0x41, 0x6e, 0x0f, 0x51, 0xcb, 0xcc, 0x24, 0x91, 0xaf, 0x50, 0xa1, 0xf4, 0x70, 0x39,
0x99, 0x7c, 0x3a, 0x85, 0x23, 0xb8, 0xb4, 0x7a, 0xfc, 0x02, 0x36, 0x5b, 0x25, 0x55, 0x97, 0x31,
0x2d, 0x5d, 0xfa, 0x98, 0xe3, 0x8a, 0x92, 0xae, 0x05, 0xdf, 0x29, 0x10, 0x67, 0x6c, 0xba, 0xc9,
0xd3, 0x00, 0xe6, 0xcf, 0xe1, 0x9e, 0xa8, 0x2c, 0x63, 0x16, 0x01, 0x3f, 0x58, 0xe2, 0x89, 0xa9,
0x0d, 0x38, 0x34, 0x1b, 0xab, 0x33, 0xff, 0xb0, 0xbb, 0x48, 0x0c, 0x5f, 0xb9, 0xb1, 0xcd, 0x2e,
0xc5, 0xf3, 0xdb, 0x47, 0xe5, 0xa5, 0x9c, 0x77, 0x0a, 0xa6, 0x20, 0x68, 0xfe, 0x7f, 0xc1, 0xad,
}
func expandKey(key []byte, t1 int) [64]uint16 {
l := make([]byte, 128)
copy(l, key)
var t = len(key)
var t8 = (t1 + 7) / 8 // effective key length in bytes
var tm = byte(255 % uint(1<<(8+uint(t1)-8*uint(t8)))) // mask for the t1 rightmost bits of the last byte
for i := t; i < 128; i++ {
l[i] = piTable[l[i-1]+l[i-t]]
}
l[128-t8] = piTable[l[128-t8]&tm]
for i := 127 - t8; i >= 0; i-- {
l[i] = piTable[l[i+1]^l[i+t8]]
}
var k [64]uint16
for i := range k {
k[i] = uint16(l[2*i]) | uint16(l[2*i+1])<<8
}
return k
}
// rotl16 rotates x left by b bits
func rotl16(x uint16, b uint) uint16 {
return (x >> (16 - b)) | (x << b)
}
func (c *rc2Cipher) Encrypt(dst, src []byte) {
if len(src) < BlockSize {
panic("rc2: input not full block")
}
if len(dst) < BlockSize {
panic("rc2: output not full block")
}
if alias.InexactOverlap(dst[:BlockSize], src[:BlockSize]) {
panic("rc2: invalid buffer overlap")
}
r0 := binary.LittleEndian.Uint16(src[0:2])
r1 := binary.LittleEndian.Uint16(src[2:4])
r2 := binary.LittleEndian.Uint16(src[4:6])
r3 := binary.LittleEndian.Uint16(src[6:BlockSize])
var j int
// perform 5 mixing rounds
for j <= 16 {
// mix up r0
r0 = r0 + c.k[j] + (r3 & r2) + ((^r3) & r1)
r0 = rotl16(r0, 1)
j++
// mix up r1
r1 = r1 + c.k[j] + (r0 & r3) + ((^r0) & r2)
r1 = rotl16(r1, 2)
j++
// mix up r2
r2 = r2 + c.k[j] + (r1 & r0) + ((^r1) & r3)
r2 = rotl16(r2, 3)
j++
// mix up r3
r3 = r3 + c.k[j] + (r2 & r1) + ((^r2) & r0)
r3 = rotl16(r3, 5)
j++
}
// perform 1 mashing round
r0 = r0 + c.k[r3&63]
r1 = r1 + c.k[r0&63]
r2 = r2 + c.k[r1&63]
r3 = r3 + c.k[r2&63]
// perform 6 mixing rounds
for j <= 40 {
// mix up r0
r0 = r0 + c.k[j] + (r3 & r2) + ((^r3) & r1)
r0 = rotl16(r0, 1)
j++
// mix up r1
r1 = r1 + c.k[j] + (r0 & r3) + ((^r0) & r2)
r1 = rotl16(r1, 2)
j++
// mix up r2
r2 = r2 + c.k[j] + (r1 & r0) + ((^r1) & r3)
r2 = rotl16(r2, 3)
j++
// mix up r3
r3 = r3 + c.k[j] + (r2 & r1) + ((^r2) & r0)
r3 = rotl16(r3, 5)
j++
}
// perform 1 mashing round
r0 = r0 + c.k[r3&63]
r1 = r1 + c.k[r0&63]
r2 = r2 + c.k[r1&63]
r3 = r3 + c.k[r2&63]
// perform 5 mixing rounds
for j <= 60 {
// mix up r0
r0 = r0 + c.k[j] + (r3 & r2) + ((^r3) & r1)
r0 = rotl16(r0, 1)
j++
// mix up r1
r1 = r1 + c.k[j] + (r0 & r3) + ((^r0) & r2)
r1 = rotl16(r1, 2)
j++
// mix up r2
r2 = r2 + c.k[j] + (r1 & r0) + ((^r1) & r3)
r2 = rotl16(r2, 3)
j++
// mix up r3
r3 = r3 + c.k[j] + (r2 & r1) + ((^r2) & r0)
r3 = rotl16(r3, 5)
j++
}
binary.LittleEndian.PutUint16(dst[0:2], r0)
binary.LittleEndian.PutUint16(dst[2:4], r1)
binary.LittleEndian.PutUint16(dst[4:6], r2)
binary.LittleEndian.PutUint16(dst[6:BlockSize], r3)
}
func (c *rc2Cipher) Decrypt(dst, src []byte) {
if len(src) < BlockSize {
panic("rc2: input not full block")
}
if len(dst) < BlockSize {
panic("rc2: output not full block")
}
if alias.InexactOverlap(dst[:BlockSize], src[:BlockSize]) {
panic("rc2: invalid buffer overlap")
}
r0 := binary.LittleEndian.Uint16(src[0:2])
r1 := binary.LittleEndian.Uint16(src[2:4])
r2 := binary.LittleEndian.Uint16(src[4:6])
r3 := binary.LittleEndian.Uint16(src[6:BlockSize])
j := 63
// perform 5 r-mixing rounds
for j >= 44 {
// unmix r3
r3 = rotl16(r3, 16-5)
r3 = r3 - c.k[j] - (r2 & r1) - ((^r2) & r0)
j--
// unmix r2
r2 = rotl16(r2, 16-3)
r2 = r2 - c.k[j] - (r1 & r0) - ((^r1) & r3)
j--
// unmix r1
r1 = rotl16(r1, 16-2)
r1 = r1 - c.k[j] - (r0 & r3) - ((^r0) & r2)
j--
// unmix r0
r0 = rotl16(r0, 16-1)
r0 = r0 - c.k[j] - (r3 & r2) - ((^r3) & r1)
j--
}
// perform 1 r-mashing round
r3 = r3 - c.k[r2&63]
r2 = r2 - c.k[r1&63]
r1 = r1 - c.k[r0&63]
r0 = r0 - c.k[r3&63]
// perform 6 r-mixing rounds
for j >= 20 {
// unmix r3
r3 = rotl16(r3, 16-5)
r3 = r3 - c.k[j] - (r2 & r1) - ((^r2) & r0)
j--
// unmix r2
r2 = rotl16(r2, 16-3)
r2 = r2 - c.k[j] - (r1 & r0) - ((^r1) & r3)
j--
// unmix r1
r1 = rotl16(r1, 16-2)
r1 = r1 - c.k[j] - (r0 & r3) - ((^r0) & r2)
j--
// unmix r0
r0 = rotl16(r0, 16-1)
r0 = r0 - c.k[j] - (r3 & r2) - ((^r3) & r1)
j--
}
// perform 1 r-mashing round
r3 = r3 - c.k[r2&63]
r2 = r2 - c.k[r1&63]
r1 = r1 - c.k[r0&63]
r0 = r0 - c.k[r3&63]
// perform 5 r-mixing rounds
for j >= 3 {
// unmix r3
r3 = rotl16(r3, 16-5)
r3 = r3 - c.k[j] - (r2 & r1) - ((^r2) & r0)
j--
// unmix r2
r2 = rotl16(r2, 16-3)
r2 = r2 - c.k[j] - (r1 & r0) - ((^r1) & r3)
j--
// unmix r1
r1 = rotl16(r1, 16-2)
r1 = r1 - c.k[j] - (r0 & r3) - ((^r0) & r2)
j--
// unmix r0
r0 = rotl16(r0, 16-1)
r0 = r0 - c.k[j] - (r3 & r2) - ((^r3) & r1)
j--
}
binary.LittleEndian.PutUint16(dst[0:2], r0)
binary.LittleEndian.PutUint16(dst[2:4], r1)
binary.LittleEndian.PutUint16(dst[4:6], r2)
binary.LittleEndian.PutUint16(dst[6:BlockSize], r3)
}

View File

@ -0,0 +1,96 @@
// Copyright 2015 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 rc2
import (
"bytes"
"encoding/hex"
"testing"
)
func TestEncryptDecrypt(t *testing.T) {
// TODO(dgryski): add the rest of the test vectors from the RFC
var tests = []struct {
key string
plain string
cipher string
t1 int
}{
{
"0000000000000000",
"0000000000000000",
"ebb773f993278eff",
63,
},
{
"ffffffffffffffff",
"ffffffffffffffff",
"278b27e42e2f0d49",
64,
},
{
"3000000000000000",
"1000000000000001",
"30649edf9be7d2c2",
64,
},
{
"88",
"0000000000000000",
"61a8a244adacccf0",
64,
},
{
"88bca90e90875a",
"0000000000000000",
"6ccf4308974c267f",
64,
},
{
"88bca90e90875a7f0f79c384627bafb2",
"0000000000000000",
"1a807d272bbe5db1",
64,
},
{
"88bca90e90875a7f0f79c384627bafb2",
"0000000000000000",
"2269552ab0f85ca6",
128,
},
{
"88bca90e90875a7f0f79c384627bafb216f80a6f85920584c42fceb0be255daf1e",
"0000000000000000",
"5b78d3a43dfff1f1",
129,
},
}
for _, tt := range tests {
k, _ := hex.DecodeString(tt.key)
p, _ := hex.DecodeString(tt.plain)
c, _ := hex.DecodeString(tt.cipher)
b, err := NewCipherWithEffectiveKeyBits(k, tt.t1)
if err != nil {
t.Errorf("New(%q, %d) failed: %v", tt.key, tt.t1, err)
continue
}
var dst [8]byte
b.Encrypt(dst[:], p)
if !bytes.Equal(dst[:], c) {
t.Errorf("encrypt failed: got % 2x wanted % 2x\n", dst, c)
}
b.Decrypt(dst[:], c)
if !bytes.Equal(dst[:], p) {
t.Errorf("decrypt failed: got % 2x wanted % 2x\n", dst, p)
}
}
}

99
pkcs/pkcs5_pbes1.go Normal file
View File

@ -0,0 +1,99 @@
package pkcs
import (
"crypto/cipher"
"crypto/des"
"crypto/md5"
"crypto/sha1"
"crypto/x509/pkix"
"encoding/asn1"
"errors"
"hash"
"github.com/emmansun/gmsm/pkcs/internal/md2"
"github.com/emmansun/gmsm/pkcs/internal/rc2"
)
var (
pbeWithMD2AndDESCBC = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 5, 1}
pbeWithMD2AndRC2CBC = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 5, 4}
pbeWithMD5AndDESCBC = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 5, 3}
pbeWithMD5AndRC2CBC = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 5, 6}
pbeWithSHA1AndDESCBC = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 5, 10}
pbeWithSHA1AndRC2CBC = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 5, 11}
)
type pbeParameter struct {
Salt []byte
Iteration int
}
// PBES1 implements the Password-Based Encryption Scheme 1.
type PBES1 struct {
Algorithm pkix.AlgorithmIdentifier
}
// Key returns the key derived from the password according PBKDF1.
func (pbes1 *PBES1) Key(password []byte) ([]byte, error) {
param := new(pbeParameter)
if _, err := asn1.Unmarshal(pbes1.Algorithm.Parameters.FullBytes, param); err != nil {
return nil, err
}
var hash hash.Hash
switch {
case pbes1.Algorithm.Algorithm.Equal(pbeWithMD2AndDESCBC) || pbes1.Algorithm.Algorithm.Equal(pbeWithMD2AndRC2CBC):
hash = md2.New()
case pbes1.Algorithm.Algorithm.Equal(pbeWithMD5AndDESCBC) || pbes1.Algorithm.Algorithm.Equal(pbeWithMD5AndRC2CBC):
hash = md5.New()
case pbes1.Algorithm.Algorithm.Equal(pbeWithSHA1AndDESCBC) || pbes1.Algorithm.Algorithm.Equal(pbeWithSHA1AndRC2CBC):
hash = sha1.New()
default:
return nil, errors.New("pkcs5: unsupported pbes1 cipher")
}
hash.Write(password)
hash.Write(param.Salt)
key := hash.Sum(nil)
for i := 1; i < param.Iteration; i++ {
hash.Reset()
hash.Write(key)
key = hash.Sum(key[:0])
}
return key, nil
}
func (pbes1 *PBES1) Decrypt(password, ciphertext []byte) ([]byte, KDFParameters, error) {
key, err := pbes1.Key(password)
if err != nil {
return nil, nil, err
}
var block cipher.Block
switch {
case pbes1.Algorithm.Algorithm.Equal(pbeWithMD2AndDESCBC) ||
pbes1.Algorithm.Algorithm.Equal(pbeWithMD5AndDESCBC) ||
pbes1.Algorithm.Algorithm.Equal(pbeWithSHA1AndDESCBC):
block, err = des.NewCipher(key[:8])
case pbes1.Algorithm.Algorithm.Equal(pbeWithMD2AndRC2CBC) ||
pbes1.Algorithm.Algorithm.Equal(pbeWithMD5AndRC2CBC) ||
pbes1.Algorithm.Algorithm.Equal(pbeWithSHA1AndRC2CBC):
block, err = rc2.NewCipher(key[:8])
default:
return nil, nil, errors.New("pkcs5: unsupported pbes1 cipher")
}
if err != nil {
return nil, nil, err
}
plaintext, err := cbcDecrypt(block, key[8:16], ciphertext)
if err != nil {
return nil, nil, err
}
return plaintext, nil, nil
}
func IsPBES1(algorithm pkix.AlgorithmIdentifier) bool {
return algorithm.Algorithm.Equal(pbeWithMD2AndDESCBC) ||
algorithm.Algorithm.Equal(pbeWithMD2AndRC2CBC) ||
algorithm.Algorithm.Equal(pbeWithMD5AndDESCBC) ||
algorithm.Algorithm.Equal(pbeWithMD5AndRC2CBC) ||
algorithm.Algorithm.Equal(pbeWithSHA1AndDESCBC) ||
algorithm.Algorithm.Equal(pbeWithSHA1AndRC2CBC)
}

View File

@ -94,6 +94,10 @@ type KDFOpts interface {
OID() asn1.ObjectIdentifier
}
type PBESEncrypter interface {
Encrypt(rand io.Reader, password, plaintext []byte) (*pkix.AlgorithmIdentifier, []byte, error)
}
// KDFParameters contains parameters (salt, etc.) for a key deriviation function.
// It must be a ASN.1-decodable structure.
// An implementation of this interface is created when decoding an encrypted PKCS#8 key.

View File

@ -20,6 +20,7 @@ type Opts = pkcs.PBES2Opts
type PBKDF2Opts = pkcs.PBKDF2Opts
type ScryptOpts = pkcs.ScryptOpts
var DefaultOpts = pkcs.DefaultOpts
var SM3 = pkcs.SM3
var SHA1 = pkcs.SHA1
var SHA224 = pkcs.SHA224
@ -54,20 +55,25 @@ func ParsePrivateKey(der []byte, password []byte) (any, pkcs.KDFParameters, erro
return nil, nil, errors.New("pkcs8: only PKCS #5 v2.0 supported")
}
if !pkcs.IsPBES2(privKey.EncryptionAlgorithm) {
return nil, nil, errors.New("pkcs8: only PBES2 supported")
var kdfParams pkcs.KDFParameters
var decryptedKey []byte
var err error
switch {
case pkcs.IsPBES2(privKey.EncryptionAlgorithm):
var params pkcs.PBES2Params
if _, err := asn1.Unmarshal(privKey.EncryptionAlgorithm.Parameters.FullBytes, &params); err != nil {
return nil, nil, errors.New("pkcs8: invalid PBES2 parameters")
}
decryptedKey, kdfParams, err = params.Decrypt(password, privKey.EncryptedData)
case pkcs.IsPBES1(privKey.EncryptionAlgorithm):
pbes1 := &pkcs.PBES1{Algorithm: privKey.EncryptionAlgorithm}
decryptedKey, kdfParams, err = pbes1.Decrypt(password, privKey.EncryptedData)
default:
return nil, nil, errors.New("pkcs8: only part of PBES1/PBES2 supported")
}
var params pkcs.PBES2Params
if _, err := asn1.Unmarshal(privKey.EncryptionAlgorithm.Parameters.FullBytes, &params); err != nil {
return nil, nil, errors.New("pkcs8: invalid PBES2 parameters")
}
decryptedKey, kdfParams, err := params.Decrypt(password, privKey.EncryptedData)
if err != nil {
return nil, nil, err
}
key, err := smx509.ParsePKCS8PrivateKey(decryptedKey)
if err != nil {
return nil, nil, errors.New("pkcs8: incorrect password? failed to parse private key while ParsePKCS8PrivateKey: " + err.Error())
@ -77,13 +83,13 @@ func ParsePrivateKey(der []byte, password []byte) (any, pkcs.KDFParameters, erro
// MarshalPrivateKey encodes a private key into DER-encoded PKCS#8 with the given options.
// Password can be nil.
func MarshalPrivateKey(priv any, password []byte, opts *Opts) ([]byte, error) {
func MarshalPrivateKey(priv any, password []byte, encrypter pkcs.PBESEncrypter) ([]byte, error) {
if len(password) == 0 {
return smx509.MarshalPKCS8PrivateKey(priv)
}
if opts == nil {
opts = pkcs.DefaultOpts
if encrypter == nil {
encrypter = DefaultOpts
}
// Convert private key into PKCS8 format
@ -92,7 +98,7 @@ func MarshalPrivateKey(priv any, password []byte, opts *Opts) ([]byte, error) {
return nil, err
}
encryptionAlgorithm, encryptedKey, err := opts.Encrypt(rand.Reader, password, pkey)
encryptionAlgorithm, encryptedKey, err := encrypter.Encrypt(rand.Reader, password, pkey)
if err != nil {
return nil, err
}

View File

@ -181,6 +181,38 @@ zOuhMC9Oo3oMYlbEXAT9mq33MkGKMUth2ek/bQIvnCHG
-----END ENCRYPTED PRIVATE KEY-----
`
const encryptedPBEWithMD5AndDES = `-----BEGIN ENCRYPTED PRIVATE KEY-----
MIGwMBsGCSqGSIb3DQEFAzAOBAhsKeK+cnfdjAICCAAEgZAE5GZMjPQCLLifGK0r
ytlpt23Qas1KI6x7qmIP6oeYflCWT0Iv7AqK2cT8YK7s5Yy3j21YiHEG5FCr8Qb+
GMlgQsRGkeU5y0I9zLZrhH9qOVJEuDLckCjMKbFXUEwx5YeBhQKTosB/quA5v9Lp
6SSLtKShYgx/MDJDarcAuj0whmNyTXijDGAMImltuqwsIUg=
-----END ENCRYPTED PRIVATE KEY-----
`
const encyptedPBEWithSha1AndDES = `-----BEGIN ENCRYPTED PRIVATE KEY-----
MIGwMBsGCSqGSIb3DQEFCjAOBAiFq+R6absk/wICCAAEgZB7T7BEaGkyMqw8xN9e
ldSPFAAXzcsPx83w1jvD8TvM7uwqUu9k0+2FnSMcuhOHjX03AFZ2JJXZZBWxZJ24
GEWLwQYJIJ16el9n2DVPkp1qqbsXPMCyHR9hW4Qxt/9aXZmTLdqpAhQ9BfTSmpQp
Yt+/s6eXMOHP2C0sp5aOxnIjkorzfgasO/Y8JtMukVlKzqU=
-----END ENCRYPTED PRIVATE KEY-----
`
const encryptedPBEWithSha1AndRC2_64 = `-----BEGIN ENCRYPTED PRIVATE KEY-----
MIGwMBsGCSqGSIb3DQEFCzAOBAhNHGnZio3lKwICCAAEgZCGUGQ3b6zS/iBlJ6BY
mpmuPBmGnuwtOtTFshWaZL8kPUROkZVBrKt6a/oM0vsTbyEDeii9ktt2cnd3plwh
fqnOmJmOBwHVeltjRLYYFzs2JgX4bXSc9eg+/AugvsDPj+dgk0yMsRLjKoZw/w/U
qZTZq/iFYg4Q80Ew7gJUBaMdA6Af8K/YDc3If7y78L0AD74=
-----END ENCRYPTED PRIVATE KEY-----
`
const encryptedPBEWithMD5AndRC2_40 = `-----BEGIN ENCRYPTED PRIVATE KEY-----
MIGwMBsGCSqGSIb3DQEFBjAOBAiZRjrXPXpYwgICCAAEgZBilgJ9rYtNt7Ih59zF
jnErhxny2wRPoK1Ng/bLNijlBnppryizyAHuujNmpDRf77pYFmszaaprWZDs2bQw
tlhqw20XVOBG2PhHXsL9LXfbm7lJOVpMtBYtbduascC1aA5Dref9L3nBNlP5zdMp
TFgdUgkic4/tuw6b5E3Ysn3ugAlPTAMm7b8Nd0Hs0P/81nA=
-----END ENCRYPTED PRIVATE KEY-----
`
func TestParsePKCS8PrivateKeyRSA(t *testing.T) {
keyList := []struct {
name string
@ -711,8 +743,8 @@ func TestParseInvalidPrivateKey(t *testing.T) {
t.Fatal(err)
}
_, err = pkcs8.ParsePKCS8PrivateKeyECDSA(data, []byte("password"))
if err == nil || err.Error() != "pkcs8: only PBES2 supported" {
t.Errorf("should be error: pkcs8: only PBES2 supported")
if err == nil || err.Error() != "pkcs8: only part of PBES1/PBES2 supported" {
t.Errorf("should be error: only part of PBES1/PBES2 supported")
}
privKey.EncryptionAlgorithm.Algorithm = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 5, 13}
@ -725,3 +757,34 @@ func TestParseInvalidPrivateKey(t *testing.T) {
t.Errorf("should be error: pkcs8: invalid PBES2 parameters")
}
}
func TestParseLegacyPBES1PrivateKey(t *testing.T) {
block, _ := pem.Decode([]byte(encryptedPBEWithMD5AndDES))
_, err := pkcs8.ParsePKCS8PrivateKey(block.Bytes, []byte("12345678"))
if err != nil {
t.Errorf("ParsePKCS8PrivateKey returned: %s", err)
}
block, _ = pem.Decode([]byte(encyptedPBEWithSha1AndDES))
_, err = pkcs8.ParsePKCS8PrivateKey(block.Bytes, []byte("12345678"))
if err != nil {
t.Errorf("ParsePKCS8PrivateKey returned: %s", err)
}
block, _ = pem.Decode([]byte(encryptedPBEWithSha1AndRC2_64))
_, err = pkcs8.ParsePKCS8PrivateKey(block.Bytes, []byte("12345678"))
if err != nil {
t.Errorf("ParsePKCS8PrivateKey returned: %s", err)
}
block, _ = pem.Decode([]byte(encryptedPBEWithMD5AndRC2_40))
_, err = pkcs8.ParsePKCS8PrivateKey(block.Bytes, []byte("12345678"))
if err != nil {
t.Errorf("ParsePKCS8PrivateKey returned: %s", err)
}
_, err = pkcs8.ParsePKCS8PrivateKey(block.Bytes, []byte("wrong pwd"))
if err == nil {
t.Errorf("should have failed")
}
}