diff --git a/README.md b/README.md index 4f7380e..fafe97a 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ I couldn't think of a better one and I wanted something notably distinct from st * To keep code light (and thus easier to debug, audit, etc.) * Because otherwise the module name is inaccurate * Because OpenSSH has their own specific variant - * Which means we can handle SSH-specific functionality if needed + * Which means we can handle SSH-specific functionality if needed * Because Golang/x/crypto has made it painfully clear that if you want something that deviates from what they think is "best practice", you need to do it yourself diff --git a/consts.go b/consts.go index f5d9364..9c324e7 100644 --- a/consts.go +++ b/consts.go @@ -1,8 +1,30 @@ package cc20p1305ssh const ( - // KeySize is 32, but OpenSSH generates two separate keys. - KeySize int = 32 + /* + FullKeySize is 64, but OpenSSH only uses the first half for the (true) KeySize. + (Normally in ChaCha20Poly1305, the second half is used for "additional data". + OpenSSH keys do not have "additional data".) + */ + FullKeySize int = 64 // Generated by Poly1305... + KeySize int = 32 // The first 32 of which are used by ChaCha20. + // And for clarity, aliases for the above. + Poly1305KeySize int = FullKeySize + ChaCha20KeySize int = KeySize + // IvSize is 0 because OpenSSH uses a fixed internal constant (see IV). + IvSize int = 0 // NonceSize is literally the only reason I need to do this. The only reason. NonceSize int = 16 + // TagLen is the length of the Poly1305 tag. + TagLen int = 16 +) + +var ( + // iv is the constant fixed IV. + iv []byte = []byte{ + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + } ) diff --git a/go.mod b/go.mod index 7dbb4c2..80e4d53 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,9 @@ module r00t2.io/cc20p1305ssh go 1.18 + +require ( + github.com/dchest/bcrypt_pbkdf v0.0.0-20150205184540-83f37f9c154a + golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e + golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a +) diff --git a/go.sum b/go.sum index e69de29..1ae911f 100644 --- a/go.sum +++ b/go.sum @@ -0,0 +1,6 @@ +github.com/dchest/bcrypt_pbkdf v0.0.0-20150205184540-83f37f9c154a h1:saTgr5tMLFnmy/yg3qDTft4rE5DY2uJ/cCxCe3q0XTU= +github.com/dchest/bcrypt_pbkdf v0.0.0-20150205184540-83f37f9c154a/go.mod h1:Bw9BbhOJVNR+t0jCqx2GC6zv0TGBsShs56Y3gfSCvl0= +golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM= +golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/notes b/notes new file mode 100644 index 0000000..e99ac67 --- /dev/null +++ b/notes @@ -0,0 +1,3 @@ += KEY SIZES = +In ChaCha20Poly1305, a key size of 32 bytes is needed. +In OpenSSH, the KDF is used to generate a 64 byte key, and the first half is used. diff --git a/testbin/main.go b/testbin/main.go new file mode 100644 index 0000000..ffbc5c1 --- /dev/null +++ b/testbin/main.go @@ -0,0 +1,127 @@ +package main + +import ( + `bytes` + `crypto/cipher` + `fmt` + `log` + + `github.com/dchest/bcrypt_pbkdf` + `golang.org/x/crypto/chacha20poly1305` +) + +var ( + EncryptedBlob []byte = []byte{ + 0x21, 0xca, 0x93, 0x7d, 0x6b, 0x81, 0x5c, 0x67, + 0x66, 0xc3, 0xd6, 0xa, 0xcc, 0x3a, 0xc7, 0x2d, + 0x7f, 0xf7, 0xae, 0x6a, 0x41, 0x85, 0x18, 0xad, + 0x37, 0x65, 0xfa, 0x8e, 0xb, 0x3b, 0x61, 0x72, + 0x70, 0xaa, 0xc5, 0x0, 0xf4, 0x43, 0xb8, 0x21, + 0xe6, 0x14, 0xe5, 0x7e, 0x81, 0x1f, 0x81, 0xe9, + 0x66, 0xa1, 0xd0, 0x44, 0x72, 0xca, 0xaf, 0xe7, + 0xbf, 0x6e, 0x95, 0x6f, 0xb3, 0x28, 0x3f, 0x92, + 0x8f, 0xc, 0x61, 0xc9, 0x39, 0x69, 0x1, 0xd5, + 0xe1, 0x43, 0x9b, 0xfe, 0x37, 0x7b, 0x4f, 0x9d, + 0xe0, 0x73, 0x33, 0x5c, 0xc9, 0x24, 0x7, 0x9c, + 0x5a, 0x96, 0x72, 0xb0, 0xe7, 0x36, 0x98, 0xc6, + 0xa, 0x63, 0x9b, 0xda, 0xf0, 0x4f, 0xa, 0x74, + 0xda, 0x77, 0xec, 0x8f, 0xc7, 0xdc, 0xe8, 0xa2, + 0x62, 0x5c, 0x19, 0x6b, 0x83, 0x41, 0xa4, 0x71, + 0x9f, 0x46, 0x78, 0x92, 0x64, 0x14, 0xc6, 0x63, + 0x41, 0x91, 0x4c, 0xe9, 0xc0, 0x9f, 0x4d, 0x33, + 0xb, 0x63, 0x1d, 0x96, 0x52, 0xa4, 0x68, 0x4a, + 0x4e, 0x13, 0xbd, 0xff, 0x64, 0x8a, 0xa7, 0xb6, + } + UnknownData []byte = []byte{ + 0x9a, 0xa8, 0xaa, 0xa3, + 0x73, 0x64, 0xb3, 0xaa, + 0x78, 0x25, 0x23, 0x48, + 0x7e, 0x79, 0x57, 0xab, + } + CombinedEncrypted = append(EncryptedBlob, UnknownData...) + + // PLAIN + PrivKey []byte = []byte{ + 0xb9, 0x9a, 0xf2, 0xcb, 0x1e, 0xb3, 0x8c, 0xff, + 0xdf, 0x76, 0x31, 0x22, 0x72, 0x56, 0xda, 0xf, + 0xe3, 0xd1, 0xdb, 0x5a, 0xff, 0xea, 0x2e, 0x44, + 0xc8, 0x5, 0x8d, 0xcc, 0xe0, 0x21, 0xae, 0x7e, + } + PubKey []byte = []byte{ + 0x9c, 0xf1, 0x14, 0x2f, 0xbb, 0x71, 0xac, 0xeb, + 0xf3, 0xae, 0xa8, 0xd8, 0x9c, 0x51, 0x8c, 0x3, + 0xa6, 0xd3, 0x66, 0x59, 0x4c, 0xee, 0xc5, 0x77, + 0x4c, 0x4f, 0xf4, 0x7d, 0x57, 0xba, 0x26, 0x75, + } + EdKey []byte = append(PrivKey, PubKey...) +) + +func main() { + + var err error + var c cipher.AEAD + var kdfSalt []byte = []byte{ + 0xfa, 0x99, 0x09, 0xfd, + 0xa2, 0xf5, 0xad, 0x42, + 0xd2, 0x8e, 0x21, 0xe3, + 0xb1, 0xcc, 0x6c, 0x94, + } + // https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.chacha20poly1305 + // https://rus-cert.github.io/ssh-chacha20-poly1305-drafts/ssh-chacha20-poly1305@openssh.html + // https://datatracker.ietf.org/doc/html/draft-josefsson-ssh-chacha20-poly1305-openssh-00 + // https://pkg.go.dev/golang.org/x/crypto/chacha20poly1305 + // var kdfKeyLen int = 512 / 8 // uses fixed 256 bits for chacha key, but it generates two keys...? + // var kdfKeyLen int = 256 // uses fixed 256 bits for chacha key + // var kdfKeyLen int = 32 // per golang pkg docs, 32 bytes (128 bits)... + var kdfKeyLen int = 64 // but per OpenSSH, 64 + // var ivLen int = 0 // ??? per golang pkg docs, 12 bytes. per ssh package, though, 8(? or 0? seems to actually be 0). but needs 16 from...somewhere. + var ivLen int = 16 + var kdfKey []byte + var subkey1, subkey2 []byte + var key []byte + var iv []byte + var nonce []byte + var plain []byte + + if kdfKey, err = bcrypt_pbkdf.Key([]byte("testpassword"), kdfSalt, 16, kdfKeyLen+ivLen); err != nil { + log.Panicln(err) + } + key = kdfKey[0:kdfKeyLen] + iv = kdfKey[kdfKeyLen:(kdfKeyLen + ivLen)] + subkey1 = kdfKey[0:32] + subkey2 = kdfKey[32:] + // iv = subkey2[0:12] + nonce = []byte{ + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + } + + fmt.Printf("kdf key (%d bytes): %#v\n", len(key), key) + fmt.Printf("kdf iv (%d bytes): %#v\n\n", len(iv), iv) + + // if c, err = chacha20poly1305.New(key); err != nil { + if c, err = chacha20poly1305.New(subkey1); err != nil { + // if c, err = chacha20poly1305.New(subkey2); err != nil { + log.Panicln(err) + } + fmt.Printf("nonce size: %d\n", c.NonceSize()) + + plain = make([]byte, len(EncryptedBlob)) + // if plain, err = c.Open(plain, iv, EncryptedBlob, UnknownData); err != nil { + // if plain, err = c.Open(plain, iv, CombinedEncrypted, nil); err != nil { + if plain, err = c.Open(plain, nonce, EncryptedBlob, nil); err != nil { + log.Panicln(err) + } + + // fmt.Printf("%#v\n", c) + + fmt.Printf("Decrypted:\n%#v\n", plain) + if bytes.Compare(plain, EdKey) != 0 { + fmt.Printf("\nOriginal:\n%#v\n", EdKey) + } + + _ = subkey1 + _ = subkey2 +}