-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
For now it implements decryption for yubikey's SHA1 challenge-response mode. The pin is also implemented as a bash script here https://github.com/anatol/clevis-extra-pins Closes #3
- Loading branch information
Showing
3 changed files
with
136 additions
and
0 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,80 @@ | ||
package clevis | ||
|
||
import ( | ||
"bytes" | ||
"encoding/base64" | ||
"encoding/hex" | ||
"fmt" | ||
"os/exec" | ||
"strconv" | ||
|
||
"github.com/lestrrat-go/jwx/jwa" | ||
"github.com/lestrrat-go/jwx/jwe" | ||
"golang.org/x/crypto/sha3" | ||
) | ||
|
||
func DecryptYubikey(msg *jwe.Message, clevisNode map[string]interface{}) ([]byte, error) { | ||
yubikeyNode, ok := clevisNode["yubikey"].(map[string]interface{}) | ||
if !ok { | ||
return nil, fmt.Errorf("clevis.go/yubikey: cannot parse provided token, node 'clevis.yubikey'") | ||
} | ||
|
||
yubType, ok := yubikeyNode["type"] | ||
if !ok { | ||
return nil, fmt.Errorf("clevis.go/yubikey: cannot parse provided token, node 'clevis.yubikey.type'") | ||
} | ||
|
||
if yubType == "chalresp" { | ||
challenge, ok := yubikeyNode["challenge"].(string) | ||
if !ok { | ||
return nil, fmt.Errorf("clevis.go/yubikey: cannot parse provided token, node 'clevis.yubikey.challenge'") | ||
} | ||
|
||
slot, ok := yubikeyNode["slot"].(string) | ||
if !ok { | ||
return nil, fmt.Errorf("clevis.go/yubikey: cannot parse provided token, node 'clevis.yubikey.slot'") | ||
} | ||
if _, err := strconv.Atoi(slot); err != nil { | ||
return nil, fmt.Errorf("clevis.go/yubikey: node 'clevis.yubikey.slot' expected to be a number") | ||
} | ||
|
||
challengeBin, err := base64.RawURLEncoding.DecodeString(challenge) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
var outBuffer, errBuffer bytes.Buffer | ||
|
||
cmd := exec.Command("ykchalresp", "-i-", "-"+slot) | ||
cmd.Stdin = bytes.NewReader(challengeBin) | ||
cmd.Stdout = &outBuffer | ||
cmd.Stderr = &errBuffer | ||
if err := cmd.Run(); err != nil { | ||
return nil, fmt.Errorf("%v: %s", err, errBuffer.String()) | ||
} | ||
// out is hex | ||
response := outBuffer.Bytes()[:40] // cut the trailing newline | ||
responseBin := make([]byte, 20) | ||
if _, err := hex.Decode(responseBin, response); err != nil { | ||
return nil, err | ||
} | ||
|
||
salt, ok := yubikeyNode["salt"].(string) | ||
if !ok { | ||
return nil, fmt.Errorf("clevis.go/yubikey: cannot parse provided token, node 'clevis.yubikey.salt'") | ||
} | ||
saltBin, err := base64.RawURLEncoding.DecodeString(salt) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
prf := sha3.New256() | ||
prf.Write(saltBin) | ||
prf.Write(responseBin) | ||
key := prf.Sum(nil) | ||
|
||
return msg.Decrypt(jwa.DIRECT, key) | ||
} else { | ||
return nil, fmt.Errorf("clevis.go/yubikey: unknown type %s", yubType) | ||
} | ||
} |
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,54 @@ | ||
package clevis | ||
|
||
import ( | ||
"bytes" | ||
"fmt" | ||
"os/exec" | ||
"strings" | ||
"testing" | ||
) | ||
|
||
func TestYubikey(t *testing.T) { | ||
inputText := "testing yubikey" | ||
|
||
clevisConfigs := []string{ | ||
`{"slot":"2"}`, | ||
} | ||
|
||
for _, c := range clevisConfigs { | ||
var outbuf, errbuf bytes.Buffer | ||
cmd := exec.Command("clevis", "encrypt", "yubikey", c) | ||
cmd.Stdin = strings.NewReader(inputText) | ||
cmd.Stdout = &outbuf | ||
cmd.Stderr = &errbuf | ||
|
||
if err := cmd.Run(); err != nil { | ||
fmt.Print(errbuf.String()) | ||
t.Fatal(err) | ||
} | ||
|
||
compactForm := outbuf.Bytes() | ||
jsonForm, err := convertToJsonForm(compactForm) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
// decrypt compact form using our implementation | ||
plaintext1, err := Decrypt(compactForm) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
if string(plaintext1) != inputText { | ||
t.Fatalf("tpm2 decryption failed: expected '%s', got '%s'", inputText, string(plaintext1)) | ||
} | ||
|
||
// decrypt json form using our implementation | ||
plaintext2, err := Decrypt(jsonForm) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
if string(plaintext2) != inputText { | ||
t.Fatalf("tpm2 decryption failed: expected '%s', got '%s'", inputText, string(plaintext2)) | ||
} | ||
} | ||
} |